同一套权重,在 Mini-SWE-Agent 下能解 62% 的留出任务,换到 Claude Code 只剩 33%。Hugging Face OpenEnv 加了一层 capture proxy:harness 仍当自己在调模型 API,代理把请求转给 vLLM,并记下采样 token id 与 logprob,TRL 就能在「人们真正用的」coding agent 里做强化学习,而不必把 harness 重写成训练环境。主源是 Clement Delangue 的发布帖、FineEnvs 的 multi-harness RL 指南,以及 TRL / OpenEnv 文档;数字以指南正文与官方文档为准。

问题不在模型卡片,在 harness

Agent harness 负责循环、工具、上下文整理和停条件。换 harness,模型看到的输入、可用的工具、以及输出格式都变。指南引用 Joel Niklaus 在 SWE-bench Pro 上的测量:同一模型 GLM-5.2,一个 harness 23%,另一个 52%。排名也不能跨模型复用,Codex 对某模型是第二好,对另一个模型落到第九。

开源权重尤其吃亏。模型若从未在你要部署的 harness 里训练过,会去调对方没有的工具名,或写出对方解析不了的格式。只在单一 harness 里做 RL,又容易学成「在那个接口约定下解题」,换环境就掉点,严重时工具调用整段失效。指南把这类现象拆成三类过拟合:动作格式、上下文结构、控制流(重试与停条件)。近年模型报告也开始标明分数来自哪个 harness;前沿团队则刻意在多个 harness 上采轨迹或做 RL。缺的是开源、可复现、能接任意你控制不了的 harness 的路径。

Capture proxy 怎么工作

生产级 coding agent(如 OpenCode)自带规划、工具和停条件。Trainer 若自己驱动多轮循环,练到的是复刻版循环;线上交付的那份 harness 往往并不在训练环里。OpenEnv 的路径是 loop-owning:agent 跑完自己的循环,trainer 只读回它做过什么。

中间那一层就是 capture proxy:

  1. Harness 拿到的是一个 base URL 和 API key。API key 实际是本轮 rollout 的 session id;未注册的 key 会 401。
  2. 代理从路径、头、请求体识别四种 API 方言:OpenAI Chat Completions、OpenAI Responses、Anthropic Messages、Gemini。
  3. 请求经 Polar 网关那套转换器变成 Chat Completions,交给你控制的 vLLM。训练采样关闭 top_p / top_k 截断,并要求引擎返回 token id 与 processed logprobs(文档示例旗标:--return-tokens-as-token-ids --logprobs-mode processed_logprobs)。
  4. 引擎侧不流式;代理收齐整段 completion,再按 harness 要求回放为流。对 harness 来说,这几乎就是普通模型服务。
  5. 每轮调用记成图上的节点:重试、子代理、上下文压缩都会分叉。从根到叶的一条路径变成训练序列,上下文 mask 为 0,采样 token mask 为 1。
  6. TRL 的 HarnessRolloutWorker 读回 trace,用会话的 verify()(agent 看不到的留出校验)打分,再交给实验性的 AsyncGRPOTrainer 做 GRPO。奖励经组内相对优势回传到被训练的 token。
vllm serve <model> \
  --enable-auto-tool-choice --tool-call-parser <parser> \
  --logprobs-mode processed_logprobs \
  --return-tokens-as-token-ids \
  --weight-transfer-config '{"backend":"nccl"}'

<parser> 按你训练的模型选。TRL 文档写明:AsyncGRPOTrainer 与 HarnessRolloutWorker 仍在 trl.experimental 下,API 可能变更。OpenCode GRPO 教程的示例是两块 GPU,一块跑 vLLM 服务策略,一块做训练;指南里的 LFM 实验也是两块 H100。OpenEnv 本体许可证是 BSD-3-Clause。托管 API 通常达不到「可训练」的 capture 等级,多半只能评测。

已验证的十个 harness

指南写明:十个 harness 已通过 capture 契约与轨迹校验(约 16,000 次 SmolDataEnvs 测试集 rollout)。OpenEnv 0.7.0 的 seams 表给出名单:

Claude Code、Gemini CLI、Codex、Mini-SWE-Agent、Qwen Code、Vibe、OpenHands SDK、OpenCode、Pi、Terminus 2。

接入方式因 harness 而异:多数只靠环境变量;Claude Code / Gemini CLI 还要在服务进程里注入;OpenCode / Pi 写沙箱内配置文件;Terminus 2 在宿主机直接传 URL 与 key。Harbor 负责任务与沙箱(任务目录含 instruction、environment、verify),OpenEnv 把 Harbor 任务暴露成统一环境并挂上代理。每个 GRPO 组内通常固定同一 harness,让优势比较的是动作,而不是外壳本身。

Clement 的发布帖举例写到 Claude Code、Codex、Hermes、Pi、OpenCode 等;站内指南「已验证十个」的权威名单以上表为准。横幅 logo 里还有 Goose、Kimi CLI、Trae 等,那是生态展示,不等于全部已过 capture 校验。

LFM2.5-2.6B 上的结果

任务来自 Kaggle 笔记改编的数据分析题集 SmolDataEnvs:训练 1,000、测试 250,四个 harness 各评一遍。基座是 Liquid AI 的 LFM2.5-2.6B(其发布说明里的训练 harness 是 Hermes Agent、OpenClaw 等,并不包含本次评测的四个)。

  • 同权重、同测试:Mini-SWE-Agent 62%,Claude Code 33%。
  • 只在 OpenCode 里 RL:OpenCode 从约 34% 提到 58%,主要抬高「老家」harness;整体约到 52%。
  • 四个 harness 同训(OpenCode、Claude Code、Codex、Mini-SWE-Agent):整体从约 42% 到 54%,四个环境都有增益。OpenCode 上的峰值仍低于 OpenCode-only,但 Claude Code、Codex 上更强。
  • 奖励除正确性(对 1 错 0)外,对已解对的题加最多 0.1 的少调用工具奖励。多 harness 模型在「基座与自己都解对」的题上,工具调用少 31%;Codex 下大约减半。OpenCode-only 在未训练过的 Claude Code 上反而可能更啰嗦。
  • 用 Qwen3.8-27B 在四个 harness 上采到 3,189 条成功 rollout 做 SFT:更好的那条(OpenCode SFT)整体停在 47.5%,低于两条 RL;多 harness SFT 甚至在 Mini-SWE-Agent 上掉了不少点。

指南强调单次评测有噪声,要看跨 checkpoint 的趋势;OpenCode-only 与 multi-harness 的整体差距本身不大,真正要看的是分布到各 harness 的形状,以及工具调用是否变短。

硬件上,本地教程与集群实验都按「推理与训练拆开」来配:一块 GPU 专供 vLLM(还要开 NCCL weight sync,让 agent 始终打到当前策略),另一块做优化器更新。Remote sandbox 路径还要准备两条 URL:trainer 连 localhost 的 vLLM,沙箱连可达的外网或隧道地址。这是工程细节,却是远程 rollout 最容易踩空的地方。

对选型与评测的启示

第一,leaderboard 上的分数要带着 harness 一起读。同一权重换外壳,差距可以大过换模型。你线上用 Claude Code 或 OpenCode,评测就应在那个外壳里跑,否则你会优化错误的接口习惯。

第二,训练环境要尽量贴近部署环境。只在极简 scaffold 里练,再塞进真实 harness,格式失败并不罕见。Capture proxy 的价值是:不必维护一堆 fork,就能在真实循环上采 token 级轨迹。Harbor 把「任务 / harness / 沙箱」拆开,同一题集可以轮换多个外壳。

第三,多 harness 训练换的是泛化,不一定是某一老家的峰值。产品若只交付一个外壳,单 harness RL 仍可能更划算;若用户会在多个外壳间切换,跨 harness 训练更有意义。SFT 模仿大模型成功轨迹更便宜,但在这组实验里整体低于 RL,且多 harness SFT 在 Mini-SWE-Agent 上出现了明显回退。

第四,奖励设计会改行为。只奖励正确性时,模型可能越解越啰嗦(指南里早期 Qwen 运行过每 rollout 调用从 13 爬到 41)。verify() 只看最终工作区;rollout_reward_fn 还能看见轨迹里的工具次数与超时。两者职责不同,不要指望环境分数单独解决「别瞎调工具」这类训练目标。

接线时以 TRL main 分支文档为准:用 partial(HarborSessionFactory, ...) 把会话工厂交给 HarnessRolloutWorker,设 harness_adapter=None 选 loop-owning 模式,再把 worker 交给 AsyncGRPOTrainer。每个会话返回 OpenEnv 的 TrainingTrace,里面已带引擎原始 token、behavior logprob 和 loss mask。rollout_reward_fn 可选,不传就直接用 verifier 的分数。旧版 OpenCode 教程里的 train_turn_fn、agent_turn_fn 在这套 API 里已经去掉,哪些 turn 吃梯度改由 OpenEnv 的 mask 决定。指南写明,截至 10 月 1 日这个 worker 只在 TRL main 上,还没进当时最新的 v1.14.1;安装前先确认你的版本里有 trl.experimental.async_grpo.openenv_harness,没有就从 main 装。Harbor 服务端的 MAX_CONCURRENT_ENVS 至少要设成 max_inflight_tasks + 1,默认值 4 比一组 8 条 rollout 还少。OpenEnv 侧常用四条命令摸底与起服:openenv harbor info、rollout、serve、push。

from functools import partial

from harbor_env.harness import HarborSessionFactory
from trl.experimental.async_grpo import AsyncGRPOConfig, AsyncGRPOTrainer
from trl.experimental.async_grpo.openenv_harness import HarnessRolloutWorker

factory = partial(
    HarborSessionFactory,
    "http://localhost:8200",  # OpenEnv Harbor server
    split="<hf-dataset>",
    harness="opencode",  # 或 "claude-code"、"mini-swe-agent" 等
    sandbox="e2b",
    llm_url=vllm_url,
    model=model,
)
worker = HarnessRolloutWorker(
    harness_session_factory=factory,
    harness_adapter=None,  # loop-owning
    rollout_reward_fn=my_reward,  # 可选:outcome -> float | None
    model_name=model,
    dataset=dataset,
    reward_funcs=[],
    processing_class=tokenizer,
    num_generations=8,
    max_inflight_tasks=8,
    vllm_server_url=vllm_url,
)
trainer = AsyncGRPOTrainer(model=model, args=config, train_dataset=dataset, rollout_worker=worker)
trainer.train()

指南的训练脚本与复现说明放在 FineEnvs 仓库固定 revision 下,避免主线漂移后命令对不上。SmolDataEnvs 的训练 / 测试集、SFT 轨迹和七个训练后 checkpoint 也已公开,便于对照原文数字。

落地时注意

  • 引擎必须返回真实 token id 与 processed logprobs;托管 API 多半只能评测,不能训。
  • 代理是单进程,文档提到高并发时健康检查会饿死、极端负载会崩;每个 rollout 要隔离沙箱与代理端口。
  • 当前推荐路径正迁到 Harbor:openenv harbor serve 统一任务、harness、沙箱;旧的 opencode_env 教程已标弃用说明,训练侧请跟 TRL 的 Harbor / async GRPO 示例。
  • 复现入口:multi-harness RL 指南、TRL OpenEnv、OpenCode GRPO 教程、OpenEnv 仓库。

如果你已经在某个真实 coding harness 里交付 Agent,可以先用同一 harness 重跑自己的评测集,再决定要不要把 capture proxy 接进训练环。

参考

  • Clement Delangue 发布帖:https://x.com/ClementDelangue/status/2107120717980471638
  • FineEnvs multi-harness RL 指南:https://huggingface.co/spaces/FineEnvs/multi-harness-rl
  • TRL OpenEnv:https://huggingface.co/docs/trl/en/openenv
  • OpenEnv OpenCode 环境:https://huggingface.co/docs/openenv/en/environments/opencode
  • OpenCode Agent GRPO 教程:https://huggingface.co/docs/openenv/tutorials/opencode-agent-grpo
  • OpenEnv 仓库(BSD-3-Clause):https://github.com/huggingface/OpenEnv
— 感谢阅读 —

一起交流

分享你的思考,让讨论更进一步。