slime Agentic RL 接入技术路线:从多轮工具调用、Agent Runtime Adapters 到 test-based reward 的完整实战指南
2026/9/16 19:08:09 网站建设 项目流程

slime Agentic RL 接入技术路线:从多轮工具调用、Agent Runtime Adapters 到 test-based reward 的完整实战指南

【免费下载链接】slimeslime is an LLM post-training framework for RL Scaling.项目地址: https://gitcode.com/GitHub_Trending/slime12/slime

slime 的定位并不只是跑单轮 RL,而是把高性能训练(Megatron 后端)、SGLang rollout serving,以及可插拔的数据生成接口组合起来,支持 agent 时代常见的多轮工具调用、sandbox 交互、subagent 分支、context compact 与 test-based reward。本文以 docs/zh/get_started/agent.md 为主线,结合仓库源码与端到端样例,给出把 agent workflow 接入 slime 的完整路线:先判断该走哪条自定义接口,再理解Sample的 token 语义与 fan-out 规则,接着掌握 Agent Runtime Adapters 与 serving 性能配置,最终落到可运行的参考样例上。读完本文,你将能够基于--custom-generate-function-path/--custom-rm-path组合,为任意多轮 agent(tool call、RAG、sandbox、subagent)编写可训练的 rollout 逻辑。

一、从哪里开始:按目标选择接入入口

agentic RL 场景五花八门,但接入 slime 的入口高度统一。原文档给出了一张"目标 → 推荐入口"对照表,这里完整保留并补充每个入口所在的文档与代码位置:

目标推荐入口
给每条 sample 跑自定义 agent loop、tool call、RAG、browser/terminal/sandbox 交互--custom-generate-function-path、编写自定义生成函数
做 verifier reward、test-based reward、环境成功判定或外部 reward 服务--custom-rm-path、编写自定义奖励函数
一个 prompt 会产生多个训练样本,例如 subagent、multi-agent、context compactcustom generate 的 fan-out 返回、examples/multi_agent
agent rollout 有长尾耗时,希望训练不要被最慢样本卡住examples/fully_async
agent 需要 sandbox、真实代码修改、测试验证和完整端到端样例examples/coding_agent_rl
多轮 agent 需要更高 SGLang serving 吞吐PD 分离、SGLang Config
想开启 SGLang 的优化 flag、router 策略或多模型 servingsglang 使用方法、SGLang Config、投机采样、低精度训练

选择原则可以概括为一句话:先用最细粒度的接口解决 per-sample 的定制,只有默认 rollout 循环本身无法表达你的 workflow 时才去覆盖整个 rollout 编排。绝大多数 agentic 任务都落在前两行的--custom-generate-function-path+--custom-rm-path组合上,这也是 slime 官方推荐的最小侵入路径。

二、推荐接入方式:从 custom generate 开始的 per-sample 定制

2.1 核心接口:--custom-generate-function-path

大多数 agentic RL 任务应该先从--custom-generate-function-path开始。这个参数只覆盖默认 rollout 函数(slime.rollout.sglang_rollout.generate_rollout)中的"生成步骤",外层的数据调度、训练循环完全复用 slime 默认逻辑,因此不会破坏原有的 RL 训练管线。其默认值为None(使用内置生成函数),函数签名如下(见 docs/zh/get_started/customization.md):

async def custom_generate(args, sample: Sample, sampling_params: dict) -> Sample | list[Sample]

这个函数负责把一次 agent 运行转换成 slime 可训练的Sample:填好tokensresponse_lengthloss_maskstatus,并在需要时填好reward或交给--custom-rm-path计算。Sample类型定义在 slime/utils/types.py,其中session_id: str | None = None字段(第 149 行)正是为多轮 agent 的会话亲和路由预留的。

2.2 训练目标必须是 token based

agent workflow 本身可以使用字符串、chat messages、tool calls、环境 observation 或框架自己的事件格式,但训练目标仍然应该是token based:尽量保留模型实际采样得到的 token ids,并用loss_mask区分可训练的模型输出和 prompt、template、tool observation、环境文本。

这一点在slime/agent/adapters/common.py的实现中体现得极为严格:adapter 从不从 response text 重新分词恢复训练目标,而是把每次 SGLang/generate调用返回的output_ids和逐 token logprobs(return_logprob=True)原样保存(见call_sglang_generateoutput_token_logprobs的处理)。loss_mask的规则是:

  • 模型生成的 token(思考、动作指令、tool call)→loss_mask = 1,参与损失计算;
  • 工具或环境返回的 token(API 结果、observation、template 文本)→loss_mask = 0,不参与损失计算;
  • loss_mask必须与response等长。

2.3 一次 rollout 拆出多个训练片段:fan-out 与 rollout_id

如果一次 prompt rollout 只对应一个训练样本,返回一个Sample即可。但在 subagent、multi-agent、context compact 等场景中,一次 rollout 会自然拆成多个可训练片段:主 agent 调用 subagent 后,subagent 的轨迹和主 agent 的后续轨迹都需要参与训练;发生 compact 后,compact 前后的上下文也被切成多个 segment。此时custom_generate直接返回list[Sample],并且这些 sibling samples 必须设置相同的rollout_id,这样 slime 在训练 step 切分和 loss 聚合时会把它们视作同一次 rollout,而不是重复计数为多次独立 rollout。

customization.md 给出的标准写法如下:

import copy from slime.utils.types import Sample async def custom_generate(args, sample: Sample, sampling_params: dict) -> list[Sample]: segments = await run_agent_and_split_segments(args, sample, sampling_params) rollout_id = sample.rollout_id if sample.rollout_id is not None else sample.index samples: list[Sample] = [] for segment in segments: s = copy.copy(sample) s.tokens = segment.tokens s.response = segment.response s.response_length = segment.response_length s.loss_mask = segment.loss_mask s.reward = segment.reward s.status = Sample.Status.COMPLETED s.rollout_id = rollout_id samples.append(s) return samples

如果一条完整 trajectory 只有一个总奖励、但被拆成了K个训练片段,常见做法是在这些片段之间分配奖励(例如每个片段写入reward / K),避免把同一次 rollout 的奖励重复放大。需要说明的是,adapter 内部路径(见下文第三节)在源码层面是"每个产出 sample 被赋予完整 reward、由 per-rollout 聚合器保证不重复计数"的语义(见 slime/agent/trajectory.py 中get_trajectory的注释),两条路径都可以实现"同一次 rollout 只计一次"的约束,自写 generate 时选择其一保持一致即可。

2.4 何时才需要--rollout-function-path

只有当你需要替换整个 rollout 编排时,才优先考虑--rollout-function-path。典型场景包括:自定义数据源调度、跨 rollout 的后台队列、完全异步生成,或者默认sglang_rollout的 prompt × sample 结构已经无法表达你的 workflow。其默认值是slime.rollout.sglang_rollout.generate_rollout,函数签名为:

def generate_rollout(args, rollout_id, data_source, evaluation=False) -> RolloutFnTrainOutput | RolloutFnEvalOutput

完整实现可参考 examples/fully_async。注意:覆盖整个 rollout function 意味着你要自行负责数据获取、生成、reward 计算等全链路,只有 per-sample 定制确实不够用时才走这条路。

2.5 与 custom rm 的配合

奖励侧通过--custom-rm-path注入。单样本模式签名为async def custom_rm(args, sample: Sample) -> float;启用--group-rm时使用批量模式async def batched_custom_rm(args, samples: list[Sample]) -> list[float]。这正好覆盖 agent 场景的三大类信号:verifier reward、test-based reward(环境成功判定)、以及调用外部 reward 模型服务。slime 内置的--rm-type选项包括mathdapodeepscalerf1gpqaifbenchremote_rm(需要--rm-url),详见 customization.md。

三、Agent Runtime Adapters:把现成 agent runtime 接进 slime

3.1 两个开箱即用的协议 adapter

slime 提供已有 agent runtime 可用的协议 adapter,位于slime/agent/adapters/

  • slime.agent.adapters.AnthropicAdapter:实现 Anthropic Messages API(/v1/messages/v1/messages/count_tokens),用于 Claude Code 风格 agent。源码见 slime/agent/adapters/anthropic.py。
  • slime.agent.adapters.OpenAIAdapter:实现 OpenAI Chat Completions 和 Responses API,用于 OpenAI SDK / OpenAI Agents SDK 风格 client。源码见 slime/agent/adapters/openai.py。

两者共享同一个BaseAdapter基类(slime/agent/adapters/common.py),该基类把 session 生命周期、per-sid 轮次上限、in-flight 任务记账和单轮_run_turn流水线全部继承下来,子类只需填充协议相关的 wire hooks(_register_routes_session_id_translate_build_reply_respond)。

重要定位:adapter 是一个便利层,不是单独的 agent framework。它的 contract 是message history in, sampled tokens out

  1. adapter 用 served 模型的 chat template 渲染当前消息历史(tokenizer.apply_chat_template),转成input_ids
  2. input_idsreturn_logprob=True调 SGLang/generate
  3. 把返回的 token ids / logprobs 导出为可训练的 trajectory segments;
  4. 不会从 response text 重新分词恢复训练目标。

这个 contract 与第二节的"token based 训练目标"原则完全一致,也是保证 RL 训练信号真实性的关键:只有模型实际采样过的 token 才能作为优化目标。

3.2 在自定义 generate 函数中使用 adapter

原文档给出了标准用法:在自定义 generate 函数里实例化对应协议的 adapter,用 aiohttp 跑它的app,然后通过 adapter 实例管理每次 rollout:

from slime.agent.adapters import AnthropicAdapter adapter = AnthropicAdapter( tokenizer=tokenizer, sglang_url=sglang_url, tool_parser=tool_parser, reasoning_parser=reasoning_parser, ) adapter.open_session(session_id, sampling_defaults=sampling_params) # Agent client 向 adapter.app 发送请求。 segments = await adapter.finish_session(session_id)

结合BaseAdapter.__init__(slime/agent/adapters/common.py),还可以补充这些构造参数:

  • max_turns_per_sid:每个 session 的轮次上限。超过上限后 adapter 会返回 429 响应来终止该 agent 运行,防止失控循环。
  • fork_threshold_tokens:trajectory fork/merge 的阈值 token 数(默认 1024),控制后续消息重分词漂移时是"合并修正"还是"分叉成独立训练片段"。
  • debug_callback:可选的调试回调,在生产环境默认不设置。

adapter 的 HTTPapp通过run_app_in_thread(见 slime/agent/aiohttp_threaded.py)跑在独立线程里,generate.py中即以此为 Claude Code 提供 Anthropic 兼容端点。

3.3 session_id 与前缀缓存亲和

多轮 agent 应使用稳定的session_id。adapter 会把它作为X-SMG-Routing-Key传给 SGLang(见call_sglang_generateheaders = {"X-SMG-Routing-Key": session_id}),让同一个 session 尽量落到同一个 worker,从而复用 prefix cache。session id 的解析规则是协议相关的:Anthropic 侧从Authorization: BearerX-Api-Keyheader 读取(sid_from_bearer),OpenAI 侧从 body 的metadata.session_id/user字段读取(sid_from_body),具体见 slime/agent/adapters/common.py。

3.4 TrajectoryManager:把多轮会话变成可训练轨迹

adapter 背后的核心数据结构是 slime/agent/trajectory.py 中的TrajectoryManager。它按session_id维护一棵 per-session 消息树:每一轮record_turn把该轮的 prompt messages 沿树向下匹配挂载,并挂上一个携带TurnRecord(prompt/output ids、logprobs、finish_reason)的 assistant 叶子节点。

由于 agent 环境的"字符串进、token 出"特性,后续轮次的 prompt 往往无法与之前保存的 token 流逐字节对上(chat template 重渲染、client 重放消息等都会造成重分词漂移)。_SampleBuilder.classify_token_drift把漂移分为三类(源码中的DriftKind):

  • CLEAN:新 prompt 精确扩展已保存 token 流,直接追加尾部;
  • REALIGN:漂移落在最近一次 response 区间内且较短,用 prompt 覆盖该区间并标记loss_mask=0继续累积;
  • FORK:漂移过大或过早,关闭当前 builder 新建一个——这个边界就是一次 fork,对应 subagent 分派或 auto-compaction 产生的分支。

get_trajectory最终把每棵树的每条 root-to-leaf 链线性化成Sample列表。其中response_trained标志保证:被多个 sibling leaf 共享的生成轮次只在第一条链上参与训练,其余链将其重新输出为loss_mask=0的上下文,避免共享前缀被重复计数。对重分词漂移的正确性守卫,有专门的单测覆盖(tests/test_agent/test_trajectory_manager_branching.py),覆盖 matched prefixes、skipped turns、split-output drift、changed token counts、prompt-base restarts 等情形。

四、Agent Serving 与性能配置

agentic rollout 往往比普通单轮 generation 更依赖 serving 配置:上下文更长、多轮请求更多、请求时长分布更重尾,并且可能同时需要 actor、reference、reward 或工具侧模型。本节整理原文档给出的四类配置手段。

4.1--sglang-*:常规 SGLang server 参数透传

常规 SGLang server 参数通过--sglang-*前缀传入,slime 自动透传给 SGLang。例如:

  • SGLang 的--context-length在 slime 中写作--sglang-context-length
  • SGLang 的--mem-fraction-static写作--sglang-mem-fraction-static
  • 其他如--sglang-log-level INFO--sglang-kv-cache-dtype fp8_e4m3同理(后者用于 long-context rollout 开启 FP8 KV cache)。

4.2--router-*:会话亲和路由

router 参数通过--router-*传入。多轮 agent 建议使用--router-policy consistent_hashing:slime 为每个 sample 分配唯一session_id,请求时通过X-SMG-Routing-Keyheader 传给 SGLang Model Gateway,consistent hashing 策略会把同一session_id的多轮请求确定性地路由到同一个 worker,提高 prefix cache 命中率。完整机制见 多轮 Agent 的会话亲和路由。可选的 router 策略还包括round_robin(简单轮询)与cache_aware(缓存感知路由,默认)。

4.3--sglang-config:复杂推理拓扑

更复杂的拓扑使用--sglang-config(YAML 文件):它可以描述 PD 分离、多模型 serving(actor / ref / reward 各自独立 router)、异构 server groups(不同 TP 大小、不同 worker 类型),以及每组不同的 SGLang overrides。典型的多轮 agent 配置是给 actor 模型配置prefill+decode两组 worker,并分别用overrides设置chunked_prefill_sizemem_fraction_static。配置格式与字段参考详见 SGLang Config。

4.4 PD 分离与吞吐优化

多轮或 agentic RL 通常建议评估PD 分离:prefill 是计算密集型(处理整个 prompt),decode 是内存带宽密集型(逐 token 生成),两者负载形态不同,拆开后更容易分别扩展资源(prefill 用更小 TP 提吞吐、decode 用更大 TP 降延迟)。对 rollout 吞吐敏感时,可以继续查看 投机采样 和 低精度训练。另外在训推一体化(--colocate)模式下,需要调低--sglang-mem-fraction-static(通常建议 0.8)为 Megatron 训练预留显存,详见 quick_start.md。

五、参考样例:从端到端 coding agent 到轻量入门

5.1 coding_agent_rl:最接近真实 agent RL 的端到端样例

完整的 coding-agent 样例见 examples/coding_agent_rl。它展示了一个比较接近真实 agent RL 的端到端形态:每条 sample 启动独立 sandbox,agent 使用工具修改代码,生成git diff,再在干净 sandbox 里跑测试得到 reward(防止 test-cheating)。

技术栈由三层构成:

  • generate.py:per-samplegenerate(),通过--custom-generate-function-path examples.coding_agent_rl.generate.generate注册。流程为prepare_workspace→ harness 运行(claude-code / codex CLI)→git_diff捕获 patch →run_evaluation打分 →adapter.finish_session产出 Sample,见 examples/coding_agent_rl/generate.py。
  • slime.agent.harness:与 harness 无关的 coding-agent 生命周期(安装 CLI、写配置、spawn 独立进程、轮询完成标记)。BaseHarness定义契约,CLAUDE_CODE/CODEX是内置实现,新增 harness 只需一个新文件。
  • slime.agent.sandbox.Sandbox:统一的 sandbox 契约(exec/write_file/read_file),E2BSandbox是 E2B 实现。换用 Docker / Modal / 本地 VM 时只需重实现这几个方法,generate.py无需改动,见 examples/coding_agent_rl/README.md。

该样例也演示了agent fan-out 的训练方式:middleware 会把 trajectory 切成subagentwipe(compact 前被冻结的链)和final等片段,generate()返回list[Sample],并让这些片段共享同一个rollout_id。启动前需要配置的 SWE 相关环境变量(ADAPTER_PUBLIC_HOSTE2B_API_KEYSLIME_AGENT_SANDBOX_IMAGE_METADATA_KEYSLIME_AGENT_NODE_TARBALLSLIME_AGENT_CC_TARBALL等)以及数据集格式(prompt/label/metadata,metadata 内含imageworkdirproblem_statement与 grader 字段),均以表格形式完整记录在 examples/coding_agent_rl/README.md 中,这里不再赘述。

需要注意,该样例要求 SGLang server 暴露与被服务模型匹配的解析器,例如 Qwen3.6 场景下:

SGLANG_ARGS=( --sglang-tool-call-parser qwen3_coder --sglang-reasoning-parser qwen3 ... )

--rollout-max-response-len是每轮传给 SGLang/generatemax_new_tokens上限,--rollout-max-context-len是多轮 prompt+response 的预算,仅在生成期生效:每轮把max_new_tokens钳制到剩余上下文长度。

5.2 轻量入门:search-r1 / retool / multi_agent

如果你只需要更轻量的入门例子:

  • examples/search-r1:多轮工具调用的最小复现,通过--custom-generate-function-path接入搜索增强的多轮生成(generate_with_search.py),外层仍走 slime 默认sglang_rollout。它同时演示了数据准备(把session_idtool_code等额外信息聚合进metadata字段,用--metadata-key metadata映射到Sample.metadata)、交互循环与 loss masking 的完整写法,对应的理论说明在 quick_start.md 的 Multiturn 适配。
  • examples/retool:工具增强生成,聚焦 SFT/RL 两阶段的数据处理与 tool sandbox。
  • examples/multi_agent:多 agent 模式,generate_with_multi_agents通过同一接口实现 per-sample 多 agent 生成,并通过MULTI_AGENT_CONFIGS配置并行数(num_parallel)与正误奖励权重(correct_reward_weight/incorrect_reward_weight)。

六、接口契约测试:验证你的自定义实现

接入 agentic workflow 时最容易出错的是自定义函数的签名与返回结构。slime 提供了一组CPU 契约测试(无需 GPU),通过字符串形式的导入路径动态加载组件,既能回归仓库内置 hook,也能验证用户通过和训练时完全相同的 CLI 参数传入的自定义实现。测试统一放在tests/plugin_contracts/下:

  • tests/plugin_contracts/test_plugin_generate_contracts.py:覆盖--custom-generate-function-path
  • tests/plugin_contracts/test_plugin_path_loading_contracts.py:覆盖--eval-function-path--custom-rm-path--dynamic-sampling-filter-path--buffer-filter-path--data-source-path等;
  • tests/plugin_contracts/test_plugin_rollout_contracts.py:覆盖--rollout-function-path
  • tests/plugin_contracts/test_plugin_runtime_hook_contracts.py:覆盖--custom-reward-post-process-path--custom-convert-samples-to-train-data-path等。

本地运行全部契约测试:

python -m pytest \ tests/plugin_contracts/test_plugin_rollout_contracts.py \ tests/plugin_contracts/test_plugin_generate_contracts.py \ tests/plugin_contracts/test_plugin_path_loading_contracts.py \ tests/plugin_contracts/test_plugin_runtime_hook_contracts.py

验证自定义实现时,只需把插件路径替换成你的模块路径,例如:

python tests/plugin_contracts/test_plugin_rollout_contracts.py \ --rollout-function-path my_project.custom_rollout.generate_rollout

此外,多轮 agent 专用的 trajectory 语义(fork、merge、loss mask 重排)由 tests/test_agent/test_trajectory_manager_branching.py 与 tests/test_agent/test_adapters.py 保障,前者覆盖分支合并与 token 漂移的正确性,后者覆盖两个协议 adapter 的翻译与回包行为。

七、路线图小结

把 agent workflow 接进 slime 的决策顺序可以归纳为五步:

  1. 选接口:per-sample 定制用--custom-generate-function-path(生成)+--custom-rm-path(奖励);要换整体编排才用--rollout-function-path;要自定义数据调度用--data-source-path
  2. 遵守 token 语义:只把模型实际采样的 token 作为训练目标,loss_mask=1只给模型输出,环境/工具/模板文本一律loss_mask=0
  3. 处理 fan-out:一次 rollout 拆多个训练片段时返回list[Sample]并共享rollout_id,奖励分配与聚合保持一致。
  4. 复用 agent runtime:Claude Code 风格用AnthropicAdapter,OpenAI SDK 风格用OpenAIAdapter,稳定session_id+consistent_hashing路由提升 prefix cache 命中率。
  5. 按需调 serving:多轮场景评估 PD 分离与--sglang-config多模型拓扑,必要时叠加投机采样与低精度训练。

从 docs/zh/get_started/agent.md 出发,配合 customization.md 的完整接口参考、quick_start.md 的 Multiturn 适配教程,以及examples/下的四个参考样例,即可把任意 agent workflow 以最小侵入方式接入 slime 的 RL 训练闭环。

【免费下载链接】slimeslime is an LLM post-training framework for RL Scaling.项目地址: https://gitcode.com/GitHub_Trending/slime12/slime

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询