Prime Intellect 发布 verifiers 0.2.0 预览版,拆分 taskset 与 harness 支持智能体 RL
verifiers v1: Decomposing Tasksets and Harnesses for Agentic RL & Evaluations
Prime Intellect 发布 verifiers 0.2.0 预览版,以 verifiers.v1 命名空间重写核心,把环境拆成 taskset、harness 和 runtime 三层,实现任意 taskset 与兼容 harness 的组合运行。
原文给出 taskset、harness、runtime 三层拆分与可组合设计,读者可据此判断智能体训练与评测流程的改造方向。
verifiers v1:为智能体强化学习与评估分解任务集和测试框架
今天,我们推出 verifiers 0.2.0,它预览了一个重写的核心,旨在为现代智能体训练和评估提供可组合的任务集和测试框架。
现代评估不仅仅是提示词。它们涉及运行编码智能体,这些智能体附带工具和自定义逻辑,例如压缩技术或内置子智能体。能够轻松运行任意任务对于现代评估和训练至关重要。
verifiers 的下一个演进版本(“v1”)正是为此而构建。其核心将环境拆分为任务集、测试框架和运行时。任务集定义了要完成的工作,即数据、工具和评分。测试框架是解决任务并产生 rollout 的程序:ReAct 循环、基于 CLI 的测试框架(如 Codex 或 Terminus 2),或你自己的智能体。rollout 发生在运行时内,运行时可以是本地的(作为子进程或 Docker),也可以在沙箱中,例如 Prime Sandboxes。

v1 将几个新组件整合在一起:
- 可组合的任务集 × 测试框架:任何任务集都可以在任何兼容的测试框架下运行
- 可替换的运行时:测试框架、工具和用户都在统一的
Runtime契约下运行 - 一等公民的分支:rollout 不假设是线性的——压缩和子智能体是原生支持的
- 训练就绪的轨迹:轨迹通过
renderers携带训练就绪的 token 和 logprobs
由于这些变更影响广泛,我们以新的 verifiers.v1 命名空间向社区发布预览版本。我们推出时即支持完整的 prime-rl 训练。
我们强烈鼓励在新抽象之上进行构建,过去几周我们一直将其用于所有生产训练和评估需求。旧代码路径现已冻结,将不再积极维护。我们的目标是在未来完全弃用它。
架构
真正可替换的任务集和测试框架面临的一个核心挑战是,我们必须对测试框架内部发生的事情做尽可能少的假设。测试框架可能只是一次模型调用,但也可能是一个功能齐全的编码智能体。这类智能体可能使用不同的模型 API 以及子智能体或压缩策略等高级功能。
在本节中,我们将深入探讨一些技术细节,这些细节使该架构能够胜任大规模训练和评估。
运行时
到目前为止,环境构建者必须自己管理运行时生命周期。编写沙箱环境意味着在正确的时机设置沙箱、正确处理瞬态基础设施错误,并可靠地清理资源。
借助新的 Runtime 抽象,这些关注点被移入框架内部,让环境构建者可以专注于任务特定逻辑,而不是基础设施和网络。核心运行时 API 由 run、read 和 write 组成。框架确保每个 rollout 获得隔离的运行时,并妥善处理基础设施错误。
class Runtime(ABC):
# lifecycle
async start() -> None # provision runtime resource
async stop() -> None # free runtime resource
cleanup() -> None # sync, idempotent teardown
# user-facing
async run(argv, env) -> ProgramResult
async read(path) -> bytes
async write(path, data) -> None可以大致区分:
- 本地运行时——例如用于最简单工作负载的
subprocess,或用于容器化任务小型实验的docker。本地运行时非常适合本地调试和开发,但不推荐用于生产规模的评估或训练。 - 远程运行时(沙箱)——例如
prime或modal提供可水平扩展的运行时资源。它们是稳定运行现代基准测试和训练工作负载的必要条件。
拦截
让 v1 设计得以运作的核心抽象是拦截服务器(interception server)的概念。拦截服务器是由 verifiers 管理的 HTTP 服务器。

一旦在运行时中于某个 agent 的 harness 里设置好任务,agent 就开始向推理服务器发起请求。verifiers 将这些请求路由经过拦截服务器,由拦截服务器代理转发请求到推理服务器,并代理返回推理服务器的响应。因此,拦截服务器位于 agent 运行时与推理之间。这样做有几个关键优势:
- 我们可以实时记录 agent 轨迹
- 我们可以在那些未必暴露这些设置的 harness 中设置采样参数
- 我们可以拦截并重写服务器端工具响应,例如用于缓解奖励黑客行为
出于可扩展性的考虑,拦截服务器是多路复用的,这意味着一个服务器处理固定数量的 rollout(默认:32)。拦截服务器池会根据观察到的并发量弹性扩缩容,从而即使在极高并发下也能保证可扩展性。
方言
并非所有 agent 都说同一种语言。如今,一个 harness 可能支持以下一种或多种方言:
- OpenAI Chat Completions(
/v1/chat/completions) - OpenAI Responses(
/v1/responses) - Anthropic Messages(
/v1/messages)
由于某些 agent 强制使用单一方言(例如 Claude Code 要求 Anthropic Messages,或 Codex 要求 OpenAI Responses),构建真正与 harness 无关的运行时意味着要正确支持所有这些请求和响应类型。
在 verifiers 中,方言是一个适配器,使我们能够检测线上消息类型,并将其动态转换为规范的 vf.types。实现这一点的核心部分是类级别的 routes 注册表(我们通过匹配它来检测应使用哪种方言),以及 parse_request 和 parse_response 方法(它们验证方言特定的线上载荷,并将其规范化为我们的规范 vf.Messages 和 vf.Response 类型)。
class Dialect(ABC, Generic[ReqT, RespT]):
# --- registration / routing (class-level) ---
routes: ClassVar[tuple[str, ...]]
# --- wire → vf ---
@abstractmethod parse_request(body) -> tuple[vf.Messages, list[vf.Tool] | None]
@abstractmethod parse_response(resp) -> vf.Response这让你可以将任务设置 + 评分逻辑编写为精简 vf.types 的函数,而与所测试的 agent 无关。
客户端
拦截服务器必须拥有一个客户端来中继(并转换)传入的请求和响应。我们识别出两种具有不同需求的主要用例:
- 评估(
EvalClient)—— 盲 HTTP 代理 - 训练(
TrainClient)—— 包装renderers客户端,以实现忠实的 token-in RL 训练
在评估期间,目标是精确中继 agent 的请求和响应,以便我们的拦截机制不会干扰 agent 的 rollout。
然而,在训练期间使用环境时,我们希望获得忠实的 token-in-token-out 表示。为此,我们使用 renderers 库:客户端渲染器对不断增长的提示前缀进行分词,并调用 vLLM 服务器上的原始 token-in 生成端点。它返回精确的 token_ids 和 logprobs,这些会被记录下来,并在 agentic rollout 的未来轮次中复用。
轨迹
v1 rollout 的主要产物是 Trace。它是 rollout 期间累积的所有数据的唯一事实来源,包含评估和训练所需的一切。它是一个严格类型的 Pydantic 模型,使下游应用(例如 prime-rl)能够轻松消费和处理这些数据。
消息图
trace 上的核心数据结构是其消息图。消息图表示消息节点的有向无环图(DAG)。每条消息都是唯一的,并链接到其前驱,确保不会保存冗余信息。与朴素地记录请求和响应对象中的 prompt 和 completion 相比,这是一项关键优化。
- v0 —— 每一轮都包含 prompt-completion 对,导致轮数呈二次方膨胀。

- v1 —— 在消息图中每条消息都是唯一的,确保 trace 大小随轮数线性增长。

为了说明长时程训练的收益究竟有多大,我们使用 N 轮构建合成的 v0/v1 trace,每轮约 1K token,并测量在不同轮长度下表示线性轨迹的大小。

在存储大量训练数据时,例如 router replay 期间的 experts 或多模态数据,差距会进一步拉大。这是一项核心基础设施优化,使我们能够毫无顾虑地在数百轮上训练 agent。
分支
并非所有 trace 都是线性的。例如,harness 可能会进行压缩或调用子 agent。在这种情况下,某条消息在图中可能没有前缀。我们称此时发生了一个分支。
分支是消息图上的任意一条根到叶路径。每个分支代表 rollout 期间的一条线性轨迹,因此可以作为一个连续的样本进行训练。这意味着具有 N 个分支的 trace 会产生 N 个可训练样本。这使得跨压缩和子 agent 的训练变得可行,并解锁了超出模型上下文窗口的长时程训练。
以压缩为例:初始的系统消息(S1)和用户消息(U1)定义了任务。agent 与其工具(A1,T1,…,TN)反复迭代,直到达到压缩限制,此时 harness 注入一条压缩 prompt(UC),agent 以摘要(AC)作为回应。在下一轮中,我们发送 (S1, U1'):harness 系统 prompt 以及包含 agent 摘要的新用户消息。

我们可以看到,harness 级别的系统 prompt S1 是唯一的,并且是所有压缩分支的根。除此之外,每个压缩分支都由唯一的消息节点组成,并产生一个独立的训练样本。
API
我们简要概述 taskset 的 API 表面,这也是大多数现有环境将迁移到的目标。
Tasksets
一个 Taskset 会生成一个带类型的 Task 对象列表。任务定义了诸如 setup 和评分逻辑等行为,并由 TaskData 作为种子。重要的是,它与将用于解决任务的 harness 无关。
例如,以下 taskset 使用精确匹配奖励定义了简单的加法任务。同一个 taskset 既可以在完全没有工具的 null harness 上运行,也可以在诸如 kimi-code 这样的大型 agent harness 上运行,用于评估和训练。
import verifiers.v1 as vf
# The data for a given task
class AdditionData(vf.TaskData):
answer: int
# A runnable task instance
class AdditionTask(vf.Task[AdditionData]):
# @vf.reward denotes the scoring function for the task.
# It needs the trace, which contains the whole message graph, including function calls, user messages etc.
# It returns the reward for the single task based on this function.
@vf.reward
async def exact_match(self, trace: vf.Trace) -> float:
return float(trace.last_reply == str(self.data.answer))
# The taskset defines the tasks and needs a vf.TasksetConfig, which can be empty.
class AdditionTaskset(vf.Taskset[AdditionTask, vf.TasksetConfig]):
def load(self) -> list[AdditionTask]:
return [
AdditionTask(
AdditionData(idx=i, prompt=f"What is {i} + {i}?", answer=2 * i),
self.config.task,
)
for i in range(100)
]
# Export the Taskset for verifiers to find it when loading
__all__ = ["AdditionTaskset"]与之前一样,verifiers 允许你添加自定义工具、模拟用户、记录指标并设计复杂的奖励。所有这些逻辑都属于任务,而 rollout 策略则留在 harness 中。有关 v1 taskset 的示例,请查看 research-environments。
Harbor
Harbor 作为社区中 agentic 任务的抽象已获得大量关注。verifiers v1 内置了一个 harbor taskset,使得添加任何 Harbor 数据集都变得轻而易举。例如,将 Terminal Bench 2 移植到 verifiers v1 只需这些代码:
import verifiers.v1 as vf
from verifiers.v1.tasksets.harbor import HarborConfig, HarborTask, HarborTaskset
# Set the dataset to the same name as registered in the Harbor registry
class TerminalBench2Config(HarborConfig):
dataset: Literal["terminal-bench/terminal-bench-2"] = "terminal-bench/terminal-bench-2"
# The data will get loaded automatically
class TerminalBench2Taskset(HarborTaskset, vf.Taskset[HarborTask, TerminalBench2Config]):
pass
# No need for a reward function, as it will get inherited from the Harbor dataset在我们的内部测试中,verifiers 在使用第三方 harness 运行相同任务时,性能与 Harbor 本身相当,这表明 verifiers 没有引入额外开销。
Harbor 是第一个获得完整支持的第三方 taskset 格式,我们还对其他流行的环境注册中心提供了 alpha 支持,例如 NeMo Gym 和 OpenEnv。
评估
一旦有了 taskset,你就可以选择要运行的 harness。我们开箱即用地支持许多流行的 harness,包括 Codex、Kimi Code、Terminus 2、Mini-SWE-Agent,同时也允许你编写自己的 harness。
要运行评估,只需准备一个 TOML
model = "nvidia/NVIDIA-Nemotron-3-Ultra-550B-A55B"
[sampling]
temperature = 1.0
top_p = 0.95
[taskset]
id = "primeintellect/terminal-bench-2"
[harness]
id = "codex"
version = "0.116.0"然后使用 CLI 运行它
uv run eval @ path/to/config.toml
训练
和之前一样,v1 环境可以直接接入我们的训练框架 prime-rl,而且集成比以往更加紧密:
- Configs — 直接从 verifiers 中读取,使环境在评估和训练中可以以相同方式进行配置
- Traces — 直接读取,无需冗余计算,例如分支可以直接从 trace 中读取,因为它们已经可用,从而显著降低 orchestrator 的内存开销。
我们已经将所有内部环境移植到新格式,并正在积极使用它们进行训练。例如,这个用于长度惩罚消融的训练配置在 6 个 H200 节点上,用自定义 harness 在 ScaleSWE 上训练 GLM-4.5-Air,并在 2 天内评估 SWE-Bench-Verified,展示了稳定的 agentic 训练,使 GLM-4.5-Air 成为更强大、更高效的编码 agent。

迈向 1.0.0
v1 作为预览版发布,我们鼓励在 verifiers.v1 之上构建新环境。未来,我们打算移除所有旧代码。
我们的 1.0.0 路线图上有以下事项:
- 多 agent 环境
- 使用任何 API 方言进行训练(当前:Chat、Responses、Messages;即将支持:Interactions)
- 对所有主要环境框架提供稳健支持,包括 OpenEnv、NeMo Gym 和 OpenReward。
在 1.0.0 发布后,旧环境仍然可以通过将 verifiers 固定到 1.0.0 之前的版本来在本地运行。
延伸阅读
- 查看 verifiers v1 的新公共文档页面,包括编写 tasksets、harnesses、CLI 使用等的用户指南
- 查看我们最近的 AIE workshop 概览视频,内容涵盖 v1 + prime-rl
- 查看 verifiers 以了解内部实现、入门示例和更新后的文档
- 查看 prime-rl 以开始训练你的 v1 环境
- 更新 prime CLI 并将 skills 与
prime lab sync同步
@article{primeintellect2026verifiersv1,
author = {Mika Senghaas and Florian Brand and Will Brown and Prime Intellect Team},
title = {verifiers v1: Decomposing Tasksets and Harnesses for Agentic RL \& Evaluations},
journal = {Prime Intellect Blog},
year = {2026},
month = {July},
note = {https://www.primeintellect.ai/blog/verifiers-v1}
}来源:Prime Intellect Blog · primeintellect.ai