产品发布 · 小互解读

Prime Intellect 开源多智能体 RL 训练框架:从「训一个 Agent」扩到「训一群 Agent 互相打交道」

新增的核心只有两个类,四个现成环境全在仓库里跑得起来
一分钟速览
  • 训 Agent 长期卡在两件事上:写死的测试把对的答案判成 0 分,静态题库几轮就被模型吃透。
  • 这次的解法是让 Agent 去治 Agent,评委进沙箱把代码真跑一遍再下判决,出题的自己把难度往「刚好卡住一半人」上靠。
  • 四个现成环境全部开源,但光是「分数该跟谁比」这一个问题,就逼出了两个新算法。
⚑ 这是 Prime Intellect 讲自己发布的能力。通篇给的是机制和代码,没有一个训练结果数字,四个环境、两个新算法都没有效果对比。
开篇

从训一个 Agent 到训一群 Agent

用强化学习训一个 Agent,长期是这么个场面:一个模型对着一份固定的题库刷,答完由一套写死的测试打分。三件事跟着卡住,测试断言的常是某个实现细节,正确的解法没按它期待的方式写就拿 0 分;题库是静态的,模型几轮就吃透;全程没有第二方,学不会跟人来回拉锯。

Prime Intellect 今天发的东西就是冲这几件事来的:训练栈从「训一个 Agent」扩到「训一群 Agent 互相打交道」,你可以编排它们之间的任意交互、指定哪几个角色参与学习,再把功劳分配到整场交互上。代码落在 verifiers v0.3.0 和 prime-rl v0.8.0,8 月 7 日发布。

有件事得先分清。多 Agent 应用编排框架关心的是让几个 Agent 配合着把活干完;这套东西关心的是活干完之后,这一场里谁该学、每个角色的功劳怎么算。后面会看到,光「分数该跟谁比」这一个问题,就逼出了两个新算法。

机制

新增的两个类:Agent 和 Env

verifiers 之前那版已经把跑一次单智能体要用的零件拆好了:一份 Taskset(要干的活)、一个 Harness模型跑在里面的那个程序,决定它能调什么工具、怎么读结果、怎么一步步往下走。模型是发动机,harness 是整台车,同一台发动机装进赛车和装进货车,能干的活完全不一样。(驱动模型的那个程序)、一个 Runtime(这个程序在哪台机器上执行)。这次做的事很朴素:给这三样找了个家,叫 Agent。

Harness 骨架 模型 Runtime 环境 Agent 可反复用的零件 run(task) Trace 轨迹 这一趟干了什么
Agent 把「哪个骨架 × 哪个模型 × 在哪儿跑」打包成一个零件,对外只露 run 这一个动作。本站示意图。

给它一个任务,还你一条 Trace一个 Agent 跑完一趟的完整记录:说了什么话、调了什么工具、拿了多少分、烧了多少 token。相当于每辆车自己的行车记录仪;一场里所有 Agent 的记录合订本,叫 Episode。(轨迹),这一趟说了什么话、调了什么工具、拿了多少分、烧了多少 token,全在里面。至于起沙箱、装工具服务、超时重试这些脏活,都被这一个动作盖住了。所以任何骨架、任何模型、任何运行环境都能任意配一遍,然后被一段普通代码指挥。

Env 是多智能体的家,签名同样一句话说完:Env.run(task, agents)。给你一个初始任务、一组已经备好的 Agent,剩下的控制流你自己编。每个 Agent 跑完,它的轨迹自动汇进一个 Episode,也就是这一场的全部记录。

verifiers v1 单智能体与多智能体的结构对照
左边是原来的单智能体:一份任务集喂给一个骨架。右边是现在:Env 里坐着几个显式的 Agent,跑完汇成一个 Episode,里面装着每个角色各自的轨迹。图:Prime Intellect

Env.run 就是普通的 async Python:要顺序跑就 await 两次,要并发跑就开个 TaskGroup,要一来一回就写个 while 循环。没有 DSL,没有图编排,没有状态机配置文件。原来的单智能体在新框架里塌缩成了一行,这层抽象没往上加任何东西。

单智能体环境的全部实现
class SingleAgentEnv(Env[SingleAgentEnvConfig]):
    async def run(self, task: Task, agents: Agents) -> None:
        await agents.agent.run(task)
整个类只有一句:让那个唯一的 Agent 把任务跑完。

还有个顺手的设计:Agent 的名字是从配置字段名自动来的。配置类里写了 solverjudge 两个字段,代码里就有 agents.solveragents.judge 可用,不用另外注册。

站内相关 Prime Agent 一个能自主改进的 Agent 框架:Opus 5 换成它后 ARC-AGI-3 评分从 30.2% 冲到 95.5% 同一家公司两天前发的骨架程序。上面说的 harness,指的就是这一类东西。
场景一

让 Agent 当评委去推翻测试

四个已实现的多智能体环境:评委、模拟用户、出题解题、回合制游戏
四个已经实现的环境,下面逐个拆。图:Prime Intellect

先说这件事有多常见。软件工程的训练任务里,对错是由测试用例判的,而测试用例经常在断言某个具体的实现细节,变量叫什么名字、函数返回的是列表还是生成器。一个把活干得漂亮的解法,只因为没按测试期待的方式写,照样拿 0 分。这个信号喂进训练,模型学到的本事就跑偏了:它练的是揣摩测试要看什么,不是把活干对。

换个大模型来打分行不行?一次调用就想给出准确判决,不现实:它看不到代码库、跑不了测试、验不了任何东西,只能对着一段文字猜。

评委做成 Agent 就不一样了。它有自己的沙箱,能翻代码、能看哪个测试挂在哪一行、能自己动手跑一遍,然后可以推翻那个死板测试的结论。

一次调用的大模型打分 只能对着一段文字猜 一段 文字轨迹 ? 评委也是一个 Agent 进沙箱把代码真跑一遍, 跑完可以推翻测试的结论 评委的沙箱 翻代码 看哪个测试挂了 自己跑一遍 判决 逐条给分
同一份解法,上面那条路只能猜,下面那条路能验。本站示意图。
评委环境的控制流(博客给的简化示意)
class AgenticJudgeEnv(vf.Env[AgenticJudgeEnvConfig]):
    async def run(self, task, agents) -> None:
        solution = await agents.solver.run(task)
        if not solution.ok:
            raise
        await agents.judge.run(JudgeTask.from_trace(solution))
解题的先跑;跑挂了整场作废;跑成了就把它的轨迹铸成一个判决任务,交给评委。

评委拿到的提示词写得很硬:

从轨迹里重建这个 Agent 做了什么,在你自己的沙箱里用真实执行去检验,凡是你能自己验的结果,绝不轻信轨迹里的说法。

verifiers 仓库里评委的评分提示词

判决怎么变成分:评委按事先定好的几条标准逐条打,写成 JSON 落到沙箱里的一个固定文件;框架严格校验,把每条判决记成解题人轨迹上的一个指标,再加权平均成一个叫 judge 的奖励。默认只认评委的分,任务集自己那套奖励权重是 0,想让两者并存,把权重调到 1 就行。

三处不在博客里的设计

评委在哪个箱子里干活,是选出来的。共享版让评委直接进解题人用过的那个沙箱,工作区原样保留,改过什么都在;隔离版给评委一个全新的箱子,只把解题人正式交上来的产物还原进去。选哪个环境 id 就等于选了这道安全边界,而不是留一个可能跟实际语义打架的开关。

评委的骨架必须能执行代码。这件事在构造环境的时候就检查,挂一个纯聊天的无工具骨架直接报错,一个不需要执行就能下的判决,那是插件式打分器,不叫 Agent 评委。

开工前先把地扫干净。解题人先用过这个箱子,所以评委动手之前,框架会把判决文件和轨迹文件先删掉。防的是两件事:解题人提前埋一份判决冒充评委的结果;或者在上传路径上放个符号链接,把评委的写操作重定向到它自己挑的文件上。

还有一条贯穿始终:评委的 token 永远不进训练数据。它是用来度量的尺子,尺子自己不该跟着被优化。

场景二

出题的 Agent 和做题的 Agent

强化学习要有用的学习信号,题目难度得贴着模型当前的水平:太简单没信息,太难也没信息。静态数据集给不了这个,它不会因为模型变强就自己变难。

做法是让模型给自己出题。一个 Agent 扮出题人,拿一个种子话题去构造新题;一组 Agent 扮解题人,各自去解。解题人答对拿分,这个好理解。出题人的分不看题目本身漂不漂亮,只看这道题对解题人群体做了什么。

全做对,说明太简单;全做错,说明太难,或者题目本身有毛病(比如出题人自己算的答案就是错的)。一半人做出来的时候,这道题给训练带来的信息最多,这个量叫 learnability(学习价值)。写成公式是 4p(1−p),p 是解出率,p=0.5 时正好等于 1。

1.0 0.5 0 0% 25% 50% 75% 100% 解题人的解出率 学习价值 一半人做出来,价值拉满 全做错:太难,或题目有毛病 全做对:太简单
出题人的奖励曲线 4p(1−p)。两头都是 0,中间拱起来,它为了拿高分,只能往「刚好卡住一半人」的难度上靠。本站按公式绘制。

这条曲线就是自动课程的全部秘密:模型变强,它出的题也跟着变难,不用人在旁边调难度。思路来自 Absolute Zero(Zhao 等人,2025)。

出题-解题环境的控制流(博客给的简化示意)
async def run(self, task: vf.Task, agents: vf.Agents) -> None:
    proposed = await agents.proposer.run(task)
    solve_task = SolveTask.from_trace(proposed)
    async with asyncio.TaskGroup() as tg:
        for _ in range(self.config.n):
            tg.create_task(agents.solver.run(solve_task))
出题人先跑,把它交出来的题铸成一个新任务,再并发派 n 个解题人各解一遍。

仓库里的实现比这段示意具体不少。种子话题有 6 个:比例混合、数论、组合、几何、概率、模算术。出题人被要求先写代码把数字构造出来、端到端验证一遍答案,最后吐一行 JSON,答案必须是一个整数,不给它蒙混的余地。默认派 4 个解题人去解同一道题。

两个座位可以配得完全不一样,这点很实用:

座位骨架跑在哪为什么这么配
出题人codex真沙箱它要写代码构造题目、验证答案,非跑不可
解题人 ×4null(无工具)不需要只做题不动手,一道题只花模型调用的钱

哪边参与学习也是一个开关:可以两边都训,也可以只训出题人,或者只训解题人。

场景三

两个模型打扑克,对手是自己

Kuhn Poker 是博弈论教科书里的玩具模型,小到可以一句话说完:总共三张牌 J、Q、K(K 最大),两个人各下 1 个筹码的底注、各摸一张暗牌,先手选「过牌」还是「下注 1 个筹码」,一两个动作牌局就结束,输赢 ±1 或 ±2 个筹码。

选它就因为它小。小到能反复跑几万手看策略往哪儿收敛,而不是把预算烧在牌局本身的复杂度上。

玩家 0 先手 过牌 下注 玩家 1 玩家 1 也过牌 → 摊牌 ±1 反手下注 → 轮回玩家 0 弃牌 → 直接收池 +1 跟注 → 摊牌 ±2 数字为玩家 0 的净筹码,两边永远相反
一手牌最多走两步就结算,五个终局全在这儿。本站按环境规则绘制。

环境管三样东西:谁手里是什么暗牌、当前公开的局面、这一步有哪些合法动作,全在主机端裁判。模型只要在回复里带一个方括号动作,比如 [check];带了两个或者带错,重问一次,再不行判它弃权。任务集是无限的,每手一个随机种子,想跑多少手跑多少手。

两个座位默认绑同一个模型,这就是自我对弈:策略变强,对手也跟着一起变强,课程自己往上爬,不用另外养一个对手服务。

算法

功劳怎么分给不同角色

前面两个自我对弈的例子,都逼出了同一个问题:这次拿的分,该跟什么比。

给一次表现打分,得跟「平常水平」比才知道是好是坏,这个平常水平叫基线(baseline)。期末考 80 分是好是坏,要看班级平均分,基线就是那个平均分。GRPO 的做法正是这样:同一道题跑一组答案,谁比这组的平均分高就加强谁。

但如果全班有一半人考的是另一张卷子,平均分就没意义了。多角色训练遇到的就是这件事,而且是以两种不同的方式。

一个人的失败,不该算在另一个人头上

出题-解题这一场里,解题人的成绩只能跟「同一道题的其他尝试」比。跨题去比,等于拿一道难题的失败去惩罚一个正常发挥的解题人。出题人的成绩也只能跟「同一个种子话题出的其他题目」比。经典 GRPO 表达不了这个层级,它把一组 rollout 拉平算个平均值,不同难度的题和不同角色全混在一起。

Hierarchical GRPO 做的事就是分组:按(角色,这一场)分成若干组,每组各自算自己的基线。代码短得不像话,分组,组内减去组均值,就这些。

零和游戏里,平均分永远是 0

扑克那边是另一种失灵。零和游戏里,一组对局的奖励平均值恒等于约 0:一边赢多少另一边就输多少,不管策略是好是坏都一样。拿它当基线,等于没有基线。

更麻烦的是两个座位的奖励尺度是相反的。混在一起算,先手位那点结构性优势会被读成这个角色的永久功劳,它只是坐了个好位置,却一直在领赏。

RAE 的解法是给每个角色维护一条自己的移动平均线,滚动记住「这个角色平时大概能拿多少分」,功劳就等于这次拿了多少减去自己的平常水平。单智能体环境用它会自动退化成带移动平均基线的 REINFORCE。

经典 GRPO:一锅端的平均分 难题易题、出题人解题人,全混在一条线上比 共用基线
所有成绩落在同一条线上比高低。本站示意图。
Hierarchical GRPO:分组各算各的 解题人只跟同一道题的其他尝试比,出题人只跟同一个种子的其他题比 同一道题的 4 次尝试 同一个种子出的几道题
两组各有各的基线,互不干扰。本站示意图。
RAE:每个角色一条自己的移动平均 零和对局里组均值恒等于 0,只能拿角色自己的历史当尺子 玩家 0 的平常水平 玩家 1 的平常水平 超出自己的平常水平 = 有功劳
两条线各走各的,先手位的结构性优势不再被当成功劳。本站示意图。

两个算法都在 prime-rl 里,各自几十行。RAE 来自 SPIRAL 那篇论文(arXiv:2506.24119)。

场景四

把用户也做成一个 Agent

助手类的任务有个共同特点:没法用「一个提示词加一个回复」表示。真实的人手里有你看不到的背景,信息是一点点透露的,会对你的回答做反应,而且是他决定什么时候算办完了。

所以把用户也做成一个 Agent。一场就是用户和助手一来一回的对话,两边的轨迹都留在这一场的记录里。模拟用户默认冻结不训练,跟评委一个道理,它是环境的一部分;助手的轨迹按原任务打分,拿去训练。

信息是这么分配的。任务集里的那一行数据,被当成用户那一侧的世界来读:它的提示词变成用户系统提示里的「你的处境」,而助手拿到的是同一个任务、提示词被清空。所以助手一开始并不知道要干什么,只能靠对话摸出来。

用户这一侧:手里握着完整的情景 助手这一侧:一开始是空白 用户 Agent 问了才说,说话短 助手 Agent 只能靠一轮轮问出来 「你好,有什么可以帮你」 用户这才说出自己要什么 满意了或确信办不到,用户发一个结束标记收尾
开场白只存在于用户那一侧:助手先「接电话」,用户才开口。本站示意图。

用户的人设里写着几条规矩,用自己的话开口,说话短,保持人设,活是助手干的(任何工具调用、答案、格式都得让助手来做,不能自己代劳),细节要问了才说。目标达成了、或者确信助手办不到,就回一个结束标记收尾。默认最多 8 轮,防止对话跑飞。

模拟用户环境的控制流(博客给的简化示意)
async with (
    agents.user.interaction(user_task) as user,
    agents.assistant.interaction(assistant_task) as assistant,
):
    ask = await user.turn("Hello! How can I help you today?")
    while True:
        answer = await assistant.turn(ask.last_reply)
        if answer.terminated:
            break
        ask = await user.turn(answer.last_reply)
        if ask.terminated:
            break
两边各开一个「保持不挂断」的对话,环境在中间转述,谁说结束就收线。

这样一来,不同的用户群体、人设、隐藏目标、交互策略,都能插进同一个接口,助手那边也能单独配。它跟前面三个环境是同一套零件的不同拼法,不是另开一条评测管线。

上手

现在能拿到什么

仓库里现成的骨架有 13 个,Claude Code、Codex、Kimi Code 这些真实的编程 Agent 都在里面,直接拿来给模型套上就能用。

  • claude_code
  • codex
  • kimi_code
  • bash
  • browser_use
  • mini_swe_agent
  • hermes_agent
  • openclaw
  • terminus_2
  • rlm
  • pi
  • pool
  • null

运行环境 4 种:本地进程、Docker、Modal,还有他们自家的云沙箱。除了上面拆的四个环境,仓库里还带了一个 BestOfNEnv,同一个任务跑 n 次独立尝试,标出最好的那次、以及有没有任何一次过了阈值,做拒绝采样和 pass@k 评测用得上。

站内相关 RLM(递归语言模型):让模型写代码自己调用自己,而不是调用工具 上面那份清单里的 rlm,就是这套思路做成的骨架。

同一个 Agent 抽象,他们自己已经拿去做合成数据生成和筛选的管线了:每条轨迹格式统一、可审计,本身就是数据产物。博客最后那个例子只有十几行,Poolside 的 Laguna-S2.1 模型跑在 pool 骨架里,去 PyPI 上查 verifiers 最新发布的版本号。跟前面训练用的是同一个 Agent。

🧰 上手卡 · verifiers
价格开源免费;模型 API 和沙箱算力另算
门槛会 Python、手上有模型 API key 就能跑单机评测;要训练得再接 prime-rl。多智能体环境里评委那一侧必须跑在容器里(Docker 或他们自家云沙箱)。多智能体能力落在 verifiers v0.3.0 与 prime-rl v0.8.0,均为 2026-08-07 发布,装旧版本没有
来源
Multi-Agent Systems in PRIME-RLKonstantin Dunas、Mika Senghaas、Eli Gottlieb 与 Prime Intellect 团队·Prime Intellect 博客·2026-08
本站说明
两张结构图来自原文。代码块是博客给的简化示意(原文标注为 Illustrative example),机制细节按 verifiers 仓库 main 分支的实现描述,共享/隔离两种评委、评委骨架必须能执行代码、开工前清理判决文件、6 个种子话题、两个座位的异构配置、用户 Agent 的 8 轮上限,这些博客未展开。版本号与发布日期取自 GitHub Releases。曲线图按 4p(1−p) 绘制,其余为本站示意图。