Skip to content

Hermes 的 Atropos 集成解决了一个核心问题:传统 RL 训练只能验证"答案对不对",但对 Agent 来说更重要的是"工具调用的结果对不对"。Hermes 把整个工具调用系统(终端执行、文件读写、网页搜索、浏览器自动化)接入 Atropos 训练循环,让奖励函数能直接在模型用过的 sandbox 环境里运行测试、验证文件、执行命令——这是真实 Agent 能力评估,不是猜答案。

Hermes Agent Atropos RL 训练集成:把工具调用加入强化学习训练循环

为什么需要工具调用 RL

标准 RL 训练(如 GRPO/PPO)对 AI 助手的训练信号来自固定答案对比:模型输出 → 和正确答案比较 → 得分。这对数学题有效,但对 Agent 类任务失效:

  • "完成这个编程任务"没有固定"正确答案"
  • 模型可能走错路但最终代码可以运行
  • 需要执行代码才能知道对不对

Hermes 的解法:把工具执行环境引入奖励函数——模型用终端执行了哪些命令、创建了哪些文件,奖励函数可以在相同 sandbox 里继续用这些工具做验证。

架构总览

Atropos 框架 (atroposlib)
    └── BaseEnv(Server 管理、Worker 调度、Wandb 日志)
          └── HermesAgentBaseEnv(hermes_base_env.py)
                │  ├── Terminal 后端配置
                │  ├── 工具集分配(per-group)
                │  ├── 代理循环执行(HermesAgentLoop)
                │  └── ToolContext(奖励函数工具访问)

                ├── TerminalTestEnv(堆栈验证)
                ├── HermesSweEnv(SWE-bench 风格训练)
                └── benchmarks/
                      ├── TerminalBench2EvalEnv(89 个终端任务)
                      ├── TBLiteEnv(100 个快速任务)
                      └── YCBenchEnv(长期战略任务)

两阶段操作模式

Hermes 的 Atropos 集成支持两种运行模式,适用于不同目的:

Phase 1 — OpenAI 服务器模式(评估 / SFT 数据生成)

使用标准 chat_completions API,服务器(VLLM/SGLang/OpenRouter/OpenAI)原生处理工具调用解析,返回结构化的 tool_calls 对象。

model → /v1/chat/completions (tools=) → server → tool_calls → 执行 → 下一轮

适用场景:评估模型能力、生成 SFT 训练数据、用 OpenRouter 跑现有模型。

Phase 2 — VLLM ManagedServer(完整 RL 训练)

使用 ManagedServer 的 /generate 端点获取原始 token + logprobs,客户端侧工具调用解析器(tool_call_parsers/)从原始文本重建结构化 tool_calls

model → /generate (raw tokens) → client-side parser → tool_calls → 执行 → 下一轮

                                                             tokens/masks/logprobs → GRPO/PPO

适用场景:完整 RL 训练,需要精确 token 序列和 logprobs。

不用在代码里选择阶段HermesAgentBaseEnv._use_managed_server() 自动检测——如果挂的是 VLLM 服务器就用 Phase 2,否则用 Phase 1。

工具调用解析器

Phase 2 需要客户端侧解析器,Hermes 内置了 10+ 种主流模型的解析器:

解析器格式适用模型
hermesChatML <tool_call> XMLNousResearch Hermes 系列
mistral[TOOL_CALLS]Mistral 系列
llama3_jsonJSON tool callingLlama 3 系列
qwen / qwen3_coderQwen 格式通义千问系列
deepseek_v3 / deepseek_v3_1DeepSeek 格式DeepSeek 系列
kimi_k2Kimi K2 格式月之暗面 Kimi
glm45 / glm47GLM 格式智谱 GLM 系列

配置(在 HermesAgentEnvConfig 里):

python
tool_call_parser: str = "hermes"  # 或 "qwen3_coder", "deepseek_v3_1" 等

核心组件详解

HermesAgentLoop — 可复用多轮代理引擎

HermesAgentLoop 和 Hermes CLI 里的 run_agent.py 是同一套逻辑的 RL 版:

python
loop = HermesAgentLoop(
    server=managed,
    tool_schemas=tools,          # OpenAI 格式工具定义
    valid_tool_names=valid_names, # 允许调用的工具名集合
    max_turns=30,                 # 最多 30 轮 LLM 调用
    task_id=task_id,             # 终端/浏览器会话隔离 ID
    temperature=1.0,              # RL 训练推荐 temperature=1.0
    budget_config=budget_cfg,     # 工具输出大小预算
)
result: AgentResult = await loop.run(messages)

AgentResult 包含:

  • messages:完整对话历史(OpenAI 格式)
  • turns_used:实际用了多少轮 LLM 调用
  • finished_naturally:模型自然停止(vs 撞上 max_turns)
  • reasoning_per_turn:每轮的推理过程(支持 extended thinking)
  • tool_errors:工具执行错误记录(用于 wandb 日志)
  • managed_state:Phase 2 的 token/logprobs 数据

工具调用在独立线程池里执行(run_in_executor),避免使用 asyncio.run() 的工具(Modal、Docker 后端)在 Atropos 事件循环里死锁。

ToolContext — 奖励函数的工具访问接口

ToolContext 是 RL 训练里最关键的设计:让奖励函数能在和模型相同的 sandbox里执行任意工具。

python
async def compute_reward(self, item, result, ctx: ToolContext) -> float:
    # 在模型用过的终端里跑测试
    test_result = ctx.terminal("pytest tests/ -v")
    if test_result["exit_code"] == 0:
        return 1.0

    # 检查模型是否创建了预期文件
    file = ctx.read_file("/workspace/solution.py")
    if file.get("content") and "def solve(" in file["content"]:
        return 0.5

    # 从 sandbox 下载文件到本地做二进制验证
    ctx.download_file("/remote/output.bin", "/local/output.bin")
    # ...本地验证逻辑...

    return 0.0

ctx 可用的方法:

方法功能
terminal(cmd, timeout)在 sandbox 里执行 shell 命令
read_file(path)读取文件内容
write_file(path, content)写入文件
search(query, path)文件内容搜索
upload_file/dir()上传文件到 sandbox
download_file/dir()从 sandbox 下载文件
web_search(query)执行网络搜索
web_extract(urls)抓取网页内容
browser_navigate(url)浏览器导航
browser_snapshot()截取浏览器状态
call_tool(name, args)调用任意工具
cleanup()释放 sandbox 资源

关键设计:task_id 是模型 rollout 和 ToolContext 共享的唯一标识符,保证它们操作同一个 sandbox——模型创建的文件、跑过的进程,都对奖励函数可见。

工具集分配——每个 Group 独立采样

在 Atropos 训练中,一个 Group 包含多个并行 rollout。Hermes 在 Group 级别(不是 rollout 级别)解析工具集,所有 rollout 共享同一组工具:

python
async def collect_trajectories(self, item):
    # Group 开始时解析一次工具集
    self._current_group_tools = self._resolve_tools_for_group()
    # 所有并行 rollout 共用
    return await super().collect_trajectories(item)

工具集解析策略:

python
# 明确指定
enabled_toolsets=["terminal", "file", "web"]

# 概率采样(training diversity)
distribution="development"  # 每个 Group 随机从 distribution 配置里采样

Distribution 让不同 Group 训练不同工具组合,防止模型只学某几个工具的用法。

内置环境示例

TerminalTestEnv — 堆栈验证

最简单的环境,内置任务,不需要外部数据集:

bash
# 验证整个工具调用链是否工作
run-api
python environments/terminal_test_env/terminal_test_env.py serve

每个任务要求模型在特定路径创建文件,奖励函数读文件验证内容。用来确认"从 API 调用到 sandbox 执行到奖励计算"全链路通畅。

HermesSweEnv — SWE-bench 风格训练

bash
python environments/hermes_swe_env/hermes_swe_env.py serve \
    --openai.model_name YourModel \
    --env.dataset_name bigcode/humanevalpack \
    --env.terminal_backend modal \     # 生产环境用 Modal 隔离
    --env.max_agent_turns 30

模型收到编程任务,用 terminal/file/web 工具完成,奖励函数在相同 sandbox 运行测试套件。

TerminalBench2EvalEnv — 89 任务 Terminal 基准测试

bash
# 完整跑 89 个任务
python environments/benchmarks/terminalbench_2/terminalbench2_env.py evaluate \
    --openai.model_name anthropic/claude-opus-4.6

# 只跑特定任务(调试用)
python environments/benchmarks/terminalbench_2/terminalbench2_env.py evaluate \
    --openai.model_name anthropic/claude-opus-4.6 \
    --env.task_filter fix-git,git-multibranch

# 跳过特定任务
python environments/benchmarks/terminalbench_2/terminalbench2_env.py evaluate \
    --env.skip_tasks heavy-task,slow-task

每个任务有预构建的 Docker 镜像、自然语言指令和测试套件。验证结果会下载到本地后计算奖励(避免沙箱内网络访问不稳定)。

自定义训练环境

只需要继承 HermesAgentBaseEnv 并实现 4 个方法:

python
from environments.hermes_base_env import HermesAgentBaseEnv, HermesAgentEnvConfig

class MyEnvConfig(HermesAgentEnvConfig):
    # 继承所有标准配置字段
    # 可以加自定义字段
    pass

class MyEnv(HermesAgentBaseEnv):
    name = "my-env"
    env_config_cls = MyEnvConfig

    @classmethod
    def config_init(cls):
        # 返回 (env_config, server_configs) 元组
        env_config = MyEnvConfig(
            enabled_toolsets=["terminal", "file"],
            terminal_backend="modal",        # 生产环境用 modal/daytona
            max_agent_turns=25,
            agent_temperature=1.0,           # RL 训练保持 1.0
        )
        server_configs = [APIServerConfig(
            base_url="http://localhost:8000/v1",
            model="YourModel",
        )]
        return env_config, server_configs

    async def setup(self):
        # 加载数据集
        self.dataset = load_dataset("your-dataset")
        self.iter = 0

    async def get_next_item(self):
        item = self.dataset[self.iter % len(self.dataset)]
        self.iter += 1
        return item

    def format_prompt(self, item):
        # 把数据集条目转成 user message
        return f"请完成以下任务:{item['task_description']}"

    async def compute_reward(self, item, result, ctx: ToolContext) -> float:
        # 核心:用 ToolContext 验证结果
        output = ctx.terminal("python solution.py")
        if output["exit_code"] == 0 and item["expected_output"] in output["stdout"]:
            return 1.0
        elif output["exit_code"] == 0:
            return 0.3   # 运行了但结果不对
        return 0.0

    async def evaluate(self, *args, **kwargs):
        # 定期评估逻辑
        pass

if __name__ == "__main__":
    MyEnv.cli()

运行:

bash
# Phase 1:OpenAI 服务器
python my_env.py serve --openai.model_name anthropic/claude-opus-4.6

# Phase 2:VLLM RL 训练
run-api  # 启动 VLLM ManagedServer
python my_env.py serve

关键配置参数

参数说明推荐值
terminal_backend沙箱后端:local/docker/modal/daytona/ssh/singularitymodal(生产)
max_agent_turns每次 rollout 最多 LLM 调用次数20~30
agent_temperature采样温度1.0(RL 训练)
tool_pool_size工具执行线程池大小≥ 并发 rollout 数
distribution工具集分布名(概率采样多样性)"development"
tool_call_parserPhase 2 工具调用解析器名按模型选
terminal_timeout单条命令超时(秒)120(编译任务更长)
default_result_size_chars工具输出裁剪阈值50000

tool_pool_size 是个容易忽视的参数:89 个并发 TB2 评估任务各需一个线程做工具调用,池子太小会导致任务排队等线程,看起来像"模型在思考"其实是在等 IO。

FAQ

Q: Hermes 的 Atropos 集成和 verl、RLVR 这些框架有什么不同?

verl 等框架解决的是"奖励计算"问题(给定模型输出,计算分数),Hermes 解决的是"工具调用验证"问题——模型在 rollout 里用了哪些工具、工具执行结果如何,奖励函数可以访问同一个环境来验证。两者互补,Hermes 的 ToolContext 可以作为 verl/Atropos 的奖励函数后端。

Q: Modal 后端和 local 后端有什么区别?

local 后端直接在当前机器执行命令,多个并发 rollout 共享同一个文件系统,容易互相干扰。Modal 后端每个 rollout 启动独立的云容器,文件系统完全隔离,训练数据更干净。terminal_lifetime 参数控制容器空闲多久后销毁(默认 1 小时)。

Q: HermesAgentLoop 和 CLI 里的 run_agent.py 有什么区别?

功能上相同(都是多轮工具调用循环),但 HermesAgentLoop 是 async 的,适合 Atropos 的 asyncio 事件循环,而 run_agent.py 是 sync 的,适合 CLI 交互场景。两者共享 handle_function_call() 工具分发逻辑。

Q: 能在 RL 训练时使用 Hermes 的 Skills 系统吗?

可以,通过 enabled_toolsets 包含 skills 工具集,或直接在 tool_schemas 里加入 skill 工具定义。不过 RL 训练时通常更关注核心工具(terminal/file/web),Skills 更多是推理/写作辅助,不常用于 Agent RL 训练。


本系列文章

Provider 与成本优化

能力扩展

部署与集成

内部机制深度解析