EnvFactory: Scaling Tool-Use Agents via Executable Environments Synthesis and Robust RL

Paper: arXiv:2605.18703 Code: LARK-AI-Lab/EnvFactory Code reference: main @ 0da1a859 (2026-05-19)

1. Motivation (研究动机)

当前 Tool-Use Agent 的 Agentic RL 主要卡在两个互相耦合的瓶颈:可执行环境不够可扩展/稳定,以及训练轨迹不像真实人类意图。生产 API / MCP server 真实但成本高、网络延迟和外部状态会让 RL rollout 不稳定;LLM simulator 便宜但会 hallucinate 工具行为;已有 synthetic environment 往往是 stateless、single-turn,或依赖预先收集的文档/任务集,难以扩展到新的工具生态。

数据侧的问题同样关键:很多合成轨迹为了保证 pass rate,会把用户目标、参数、推理步骤都写得过于显式,像“执行清单”而不是真实用户请求。真实用户常常只给出简短、含糊、带上下文省略的需求,agent 必须推断隐含参数、决定是否问澄清问题、调用工具并处理状态变化。如果 RL 只见过过度指定的轨迹,模型在 BFCL multi-turn、-Bench、VitaBench 这类交互/缺参/长上下文 benchmark 上泛化会受限。

本文要解决的具体目标是:自动发现并构造 stateful、low-latency、可验证的 executable tool environments,同时从这些环境中合成自然、多轮、多步、可验证的 tool-use 轨迹,用于 SFT + RL post-training。一旦这个闭环成立,Agentic RL 不再依赖昂贵真实 API 或手工任务集,可以用较少环境数量产生更高质量 reward signal;本文最终用 85 个 verified environments、842 个 tools、1,622 条 SFT conversations 和 953 条 RL conversations,就在多个 tool-use benchmark 上提升 Qwen3 系列模型。

2. Idea (核心思想)

EnvFactory 的核心 insight 是:Tool-use RL 的规模化不应只扩大任务文本,而应先自动合成“可执行且可验证”的环境,再用环境的工具依赖拓扑去生成自然查询和 ground-truth trajectories。环境提供真实状态转移和可验证 reward,工具图保证采样出来的工具链在逻辑上可满足,query refinement 再把显式工具链转成更接近人类的隐式请求。

关键创新可以压缩为两个阶段:EnvGen 从真实在线资源出发,自动生成 MCP metadata、Pydantic 状态数据库、Python tool server,并用 Test Agent 做 validation-revision;QueryGen 将所有 tools 组织成 dependency graph,通过 topology-aware sampling 先解析内部依赖,再生成多轮用户 query、pass@k 解决轨迹、过滤/选择可验证答案,最后把这些轨迹转成 SFT/RL 数据。

与 EnvScaler / AWM 等方法相比,EnvFactory 的差别不是“多生成一些任务”,而是把环境本身变成自动化产物:EnvScaler 仍依赖较大任务/环境预算,AWM 侧重 abstract scenario seeds;EnvFactory 直接从 authentic resources 恢复工具生态,并在 stateful executable server 上验证轨迹。因此它用 85 个环境和 2,575 个总训练任务,在 Qwen3-4B 上达到 BFCL multi-turn 48.50、overall 30.77,超过或接近使用更多环境/任务的 baseline。

3. Method (方法)

3.1 Overall framework: EnvGen + ToolGraph + QueryGen + SFT/RL

论文把 tool agentic interaction 形式化为 environment、tool、agent、user 四个对象。每个环境 定义为: 其中 是环境 metadata(描述、tool definitions、schemas), 是 stateful database schema, 是 executable Python implementation, 是暴露给 agent 的 tool interface(默认 MCP)。全局工具集为 。Agent 在每步观察用户消息或 tool results 后,要么调用工具,要么自然语言回复;user 则可以补充信息、澄清、执行需要人类授权/物理动作的操作,直到达到 step 上限或 user stop。

Figure 1 解读:左侧展示 EnvGen 的三代理闭环:Search Agent 先根据已有环境覆盖缺口提出新场景并搜索 authentic sources;Code Agent 把 metadata 转成 Pydantic database schema 和 MCP tool server;Test Agent 生成测试场景、执行验证并把错误报告反馈给 Code Agent。右侧 sunburst 展示 85 个 verified environments 跨 commerce、finance、travel、office、lifestyle、research、utilities 七类领域,外圈是每个环境的 tool count,说明论文不是只在单一 toy domain 上构造环境。

整个 pipeline 的数据流是:

  1. EnvGen:构造 ,验证后加入环境池
  2. Dependency Tool Graph:把 中的 tools 与 input/output parameters 建成有向图
  3. Topology-aware Sampling:从 采样工具序列 ,保证每个 required input 要么来自用户可提供 external parameter,要么由已采样工具输出 internal parameter。
  4. QueryGen:围绕 生成 scenario、database state、多轮 query、pass@k solution trajectories,并选择/过滤可验证轨迹。
  5. Post-training:用 user-interaction trajectories 做 SFT,用 tool-call trajectories 做 RL,RL 采用 composite rule reward + GRPO。

直觉上,EnvFactory 有效的原因是它把“自然语言任务生成”从一个无约束文本生成问题,变成了“先有可执行世界,再从世界中的依赖关系反推用户意图”。这样做同时减少两类噪声:工具调用不会脱离真实状态转移,用户 query 也不会要求 agent 使用不存在或依赖未满足的工具。

3.2 EnvGen: 自动构造可执行环境

EnvGen 的输入不是固定任务文档,而是当前环境池 与可搜索的真实资源。Search Agent 分析覆盖缺口,检索 API docs、technical reports、usage examples,生成 structured metadata ,包括环境描述、tool definitions、tool schemas。随后 Code Agent 根据 推导 stateful database schema ,把工具参数、中间状态、持久记录写成 Pydantic models,并提供 load/dump 接口,保证每个 rollout session 可以隔离、复现。

Code Agent 再根据 生成 Python implementation ,并包装成标准 MCP interface 。Test Agent 生成 unit test scenarios,从四个维度验证:tool interface 是否与 metadata 一致、tools 是否能 import/execute、execution results 是否符合 expected behavior、database states 是否在 tool invocation 后正确转移。失败时,Test Agent 输出结构化 error report,Code Agent 修代码并重建环境,直到全部测试通过或达到 revision budget。

released code 中这一流程主要落在:src/gen/env_gen/env_gen.pyEnvGen.generate_mcp_env(),它顺序执行 metadata loading、MCPToolGen.gen() 生成工具代码、ScenarioGen.generate_scenarios() 生成测试场景、注册 configs/mcp_server.json,再调用 ValidateReviseGen.validate_revise_loop()src/gen/env_gen/validate_revise.py 用 semaphore 并发验证 scenarios,聚合 critical/high/medium/low errors,并用 tool reviser prompt 修复代码。

def envgen_generate_environment(metadata_path, config):
    metadata = load_metadata(metadata_path)
    result = EnvGenResult(class_name=metadata["class_name"])
 
    tool_code = MCPToolGen(config).gen(
        mcp_server_name=metadata["class_name"],
        mcp_server_description=metadata["description"],
        tools=metadata["tools"],
    )
    tool_path = save_to_envs_tools(metadata["class_name"], tool_code)
    scenarios = ScenarioGen(config).generate_scenarios(
        mcp_server_name=metadata["class_name"],
        tool_code=tool_code,
        n_scenarios=config.n_scenarios,
    )
    update_mcp_server_config("configs/mcp_server.json", metadata["class_name"], tool_path)
    client_manager = MCPClientManager().register_MCP_server(metadata["class_name"], tool_path)
 
    validation = ValidateReviseGen(client_manager, config).validate_revise_loop(
        mcp_server_name=metadata["class_name"],
        mcp_server_description=metadata["description"],
        tool_code=tool_code,
        tools_metadata=metadata["tools"],
        scenarios=scenarios,
    )
    return validation.final_code, validation.success

3.3 Dependency Tool Graph 与 topology-aware sampling

Tool graph 的目标是捕捉工具间非线性依赖。论文先用 BAAI/bge-m3 embedding 编码每个 tool 的 input/output parameters;对任意工具对 ,若某个 output parameter 与 input parameter 的 cosine similarity 超过阈值,就加入边 。由于纯语义匹配会漏掉 parameter-less tools 或同功能组内的逻辑关系,论文再让 LLM 对每个 environment 内的 tools 做 logical dependency refinement,补边和剪掉 spurious edges。

released code 的 src/graph/tool_graph.py 更细:ToolGraph.build_tool_graph() 将 Tool 和 Parameter 都作为 NetworkX nodes;input parameter 到 tool 的边带 required 属性,tool 到 output parameter 是 Tool_Output,parameter similarity 超过 build_edge_threshold=0.85 时加入 Tool_DependParameter_RelateParameter.similarity()src/graph/tool_node.py 中用 0.6 name/type similarity + 0.4 description similarity。ToolGraph.validate_parameter() 先看 parameter 是否被 LLM 标为 user_provided,再看已访问工具是否直接或间接产生该参数。

Figure 2 解读:Part 1 显示 topology-aware sampling 不是简单 random walk:当目标 tool 的输入无法由用户直接提供时,采样器会沿反向依赖边递归补足 prior tools。Parts 2–7 展示 QueryGen 后续流程:基于 sampled tools 生成 scenario 与初始 database state,再生成/refine query,部署 sandbox agent 解决 query,最后选择可验证轨迹。

Topology-aware sampling 的硬约束是:采样工具 的所有 required inputs 必须已经可由用户外部提供,或由此前采样的工具输出内部满足。论文将 input parameter 分为 external 与 internal:cityname 这类 external 参数可以来自用户;hotel_id 这类 internal 参数通常要由 get_hotel_list 等 prior tool 产生。若参数 不满足 optional / externally providable / internally satisfiable 任一条件,采样器就沿 的反向边选一个能产生 的 prior tool,并递归解析 prior 的依赖。

def topology_sample(graph, start_tool, target_n, max_depth=3):
    visited = [start_tool]
    queue = [start_tool]
 
    while len(visited) < target_n and queue:
        tool = queue.pop(0)
        priors = sample_priors(graph, tool, visited, depth=0, max_depth=max_depth)
        for prior in priors:
            if prior not in visited:
                visited.append(prior)
        if tool not in visited:
            visited.append(tool)
 
        successors = graph.successor_tools(tool, exclude=visited)
        if successors:
            queue.append(random.choice(successors))
    return visited[:target_n]
 
 
def sample_priors(graph, tool, visited, depth, max_depth):
    if depth >= max_depth:
        return []
    sampled = []
    current = set(visited)
    for param in tool.input_schema["parameters"]:
        required = graph.edge_data(param, tool).get("required", False)
        if (not required) and random.random() < 0.6:
            continue
        valid = graph.validate_parameter(param, list(current))
        if valid and random.random() < 0.9:
            continue
        candidates = graph.tools_that_output(param, target_tool=tool)
        prior = choose_with_server_limit(candidates, current, max_servers=3)
        if prior and prior not in current:
            sampled += sample_priors(graph, prior, list(current), depth + 1, max_depth)
            sampled.append(prior)
            current.add(prior)
    return sampled

3.4 QueryGen: 从工具链到自然、多轮、可验证轨迹

QueryGen 输入 sampled tool chain ,先构造 user profile 和 scenario,并生成严格符合 Pydantic schema 的 initial database state。随后它可以把工具链随机切分为多轮,每轮含 1–5 个 sampled tools。对每一轮,QueryGen 先做 subgoal decomposition,把工具调用拆成 fine-grained subgoals 与 user intents;再做 goal articulation,把这些 subgoals 组织成自然语言请求。

论文强调 calibrated refinement 的四种变换:implicit reference(把显式 ID 替换为上下文指代,省略可推断参数)、action compression(压缩可由 agent 推断的中间步骤)、ambiguity introduction(加入合理指代歧义,让 agent 必要时澄清)、goal expansion(加入主题相关的次要目标)。这一步的作用不是制造噪声,而是把工具链从“精确操作列表”变成真实用户会说的需求。

released code 的 src/gen/query_gen/query_gen_non_conv.pyquery_gen_conv.py 实现了这个状态机。QueryGen wrapper 会根据 enable_user_interaction 路由到 conversational 或 non-conversational implementation;QueryGenConfig 的默认 pass_k=4max_solve_iterations=8,而 preset_config.pySFT_CONV 开启 enable_query_refinement=Trueenable_user_interaction=Trueenable_user_tool_use=TrueRL_NON_CONV 开启 enable_filteration=True 但不做 query refinement。examples/sythesize_query.py 读取 graph.pkl,用 TopologySampler()SFT_CONV 生成 200 条 trajectories,asyncio.Semaphore(5) 控制并发。

async def querygen_one_chain(tool_graph, tool_chain, config):
    impl = QueryGen(tool_graph, config)
 
    # prepare: scenario planning, optional LLM turn split, user-tool classification
    scenario = await impl.prepare(tool_chain)
    for turn_idx, node in enumerate(tool_chain.nodes):
        # state initialization is validated by loading into each MCP server
        node.initial_scenario = await impl.schema_generate(node.mcp_servers, scenario)
 
        # generate and optionally refine natural query for target tools
        node.query, node.user_intent = await impl.generate(node.raw_tool_call)
        if config.enable_query_refinement:
            node.query, node.user_intent = await impl.refine(node.query)
 
        # pass@k solve in isolated MCP sessions, then select/filter best trace
        traces = await asyncio.gather(*[
            impl.solve(node.query, node.initial_scenario, k=k)
            for k in range(config.pass_k)
        ])
        selected = await impl.select(traces)
        if config.enable_filteration:
            selected.steps = await impl.filter(selected.steps)
        node.steps = selected.steps
        node.final_scenario = selected.final_scenario
    return tool_chain

3.5 SFT/RL 数据构造与 composite reward

SFT 与 RL 使用同一批可执行环境产生的轨迹,但 sample granularity 不同。released code 的 src/utils/data_process.py 中,convert_to_sft_data() 对每个有效 turn 的 step pairs 生成 instruction-output 样本,过滤 failed tool calls,并可保留 <think> reasoning trace;convert_to_rl_data() 则对每个 node 生成一个 RL sample,prompt 是当前历史 + query,reward_model.ground_truth 是该 turn 的 tool calls,extra_info.mcp_factory_kwargs 保存 mcp_serversinitial_configfinal_config,供 rule reward 在 executable environment 上检查。

论文的 RL reward 是三项组合:trajectory-based reward 匹配预测与 ground-truth tool-calling sequences;state-based reward 检查执行后的 final database state 是否等价;length penalty 抑制冗长调用序列。公式为: 其中 是状态等价 reward, 是长度惩罚,。这个设计针对 tool-use 的非唯一性:只看轨迹会惩罚等价顺序变化,只看最终状态又会放过不必要调用;两者加权能同时约束“做对事”和“用合理方式做”。

论文公式与 released code 实现差异:论文定义了完整 composite reward,但当前 public repo main@0da1a859 只公开了 RL sample 的 rule-reward 输入结构(ground_truth tool calls、initial/final configs)和 SFT config,未包含 VeRL 训练脚本或计算 的 reward function 源码;因此下面伪代码按论文公式和 repo 暴露的数据字段重构,不声称逐行对应 released reward evaluator。

def build_rl_sample(node, history):
    prompt = history + [{"role": "user", "content": node["query"].strip()}]
    tool_calls = [
        {
            "name": call["name"],
            "arguments": call["arguments"],
            "masked_arguments": call.get("masked_arguments", []),
        }
        for step in node["steps"]
        if step["role"] == "tool_call"
        for call in step["content"]
    ]
    return {
        "prompt": json.dumps(prompt),
        "data_source": "EnvFactory",
        "agent_name": "tool_agent",
        "ability": "tool_use",
        "reward_model": {"style": "rule", "ground_truth": json.dumps(tool_calls)},
        "extra_info": {
            "mcp_factory_kwargs": {
                "mcp_servers": json.dumps(node.get("mcp_servers", [])),
                "initial_config": json.dumps(node.get("initial_scenario", {})),
                "final_config": json.dumps(node.get("final_scenario", {})),
            }
        },
    }
 
 
def composite_tool_reward(predicted_calls, target_calls, initial_state, target_final_state,
                          alpha=0.5, gamma=0.0):
    predicted_final_state = execute_in_mcp_sandbox(initial_state, predicted_calls)
    r_traj = masked_sequence_match(predicted_calls, target_calls)
    r_state = database_state_equivalence(predicted_final_state, target_final_state)
    p_length = max(0, len(predicted_calls) - len(target_calls))
    return alpha * r_traj + (1.0 - alpha) * r_state - gamma * p_length

Code reference: main @ 0da1a859 (2026-05-19) — pseudocode and mapping based on this commit

Paper ConceptSource FileKey Class/Function
EnvGen orchestrationsrc/gen/env_gen/env_gen.pyEnvGen.generate_mcp_env()
MCP tool code generationsrc/gen/env_gen/mcp_tool_gen.pyMCPToolGen.gen(), save_tools()
Scenario validation/revisionsrc/gen/env_gen/validate_revise.pyValidateReviseGen.validate_revise_loop(), _validate_all_scenarios(), _revise_code()
Metadata/schema extractionsrc/gen/mcp_schema_gen.pySchemaGen.generate(), _extract_tools_info(), _validate_and_save_schema()
Tool/parameter graphsrc/graph/tool_graph.py, src/graph/tool_node.pyToolGraph.build_tool_graph(), _build_edge_with_sim(), validate_parameter(), Parameter.similarity()
Topology-aware samplingsrc/graph/sampler.pyTopologySampler.sample_prior(), TopologySampler.sample()
Query generation state machinesrc/gen/query_gen/__init__.py, src/gen/query_gen/query_gen_non_conv.py, src/gen/query_gen/query_gen_conv.pyQueryGen.gen(), prepare(), schema_generate(), generate(), refine(), solve(), select(), filter()
Preset trajectory configssrc/gen/query_gen/preset_config.pySFT_NON_CONV, SFT_CONV, RL_NON_CONV
SFT/RL data conversionsrc/utils/data_process.pyconvert_to_sft_data(), convert_to_rl_data()
Released SFT launch configconfigs/llamafactory_sft.yamlmodel_name_or_path, cutoff_len, batch/LR/epoch settings

4. Experimental Setup (实验设置)

数据与环境规模

EnvFactory 构造了 85 个 verified MCP environments,覆盖 commerce、finance、travel、office、lifestyle、research、utilities 七个 domains,共 842 个 tools。基于这些环境,论文合成 1,622 条 SFT conversations 和 953 条 RL conversations,总训练任务数 2,575。平均每个 conversation 有 4.82 turns,每 turn 有 3.29 steps(包含 tool calls 与 user interactions)。

Figure 5 解读:该图展示合成 conversation 的 turn/step/tool-call 分布,用于证明数据不是单步调用集合,而是包含多轮、多 tool-call、多 user interaction 的 agentic trajectories。结合 4.82 turns / 3.29 steps per turn 的统计,EnvFactory 的训练数据更接近交互式工具使用,而不是单轮 function calling。

Baselines、backbones 与 benchmarks

训练 backbone 是 Qwen3-1.7B、Qwen3-4B、Qwen3-8B。Baseline 使用 Qwen3 base checkpoints,并对比 AWM 与 EnvScaler 的可用 checkpoints。Evaluation 覆盖四类 tool-use / agent benchmark:

  • BFCL v3:报告 single-turn 与 multi-turn,其中 multi-turn 又含 Base、Miss-Func、Miss-Param、Long-Context,用于测试缺函数、缺参数和长上下文 tool-use。
  • -Bench:报告 Airline、Retail、Telecom 及平均值,强调多轮用户/环境交互。
  • VitaBench:报告 Delivery、Store/Instore、Ota 及平均 accuracy / NL assertion,衡量 conversational task completion。
  • MCP-Atlas:报告 Pass Rate 与 Mean Coverage;由于网络约束,论文评估 30/36 servers、291/500 tasks,排除 mongodboxylabsbrave-searchwikipediaslackgoogle-workspace

Training config 与 compute

论文训练分两阶段:Stage 1 用 user interaction trajectories 做 SFT;Stage 2 用 tool-call trajectories 做 RL。SFT 使用 LlamaFactory,RL 使用 VeRL + GRPO。论文 appendix 报告:SFT data synthesis 使用 Qwen3-30B-A3B-Thinking-2507,部署在 GB GPUs,生成 1,000 条 multi-turn/multi-step trajectories 约需 20 GPU hours;SFT 训练 Qwen3-4B 时使用 GB GPUs、learning rate 、batch size 256、3 epochs,约 10 GPU hours;RL 训练 Qwen3-4B 时使用 GB GPUs、learning rate 、rollout size 8、batch size 256、max trajectory length 16k、max generation length 4k、10 epochs,约 20 GPU hours。

released config 需要单独看:configs/llamafactory_sft.yaml 当前指向 model_name_or_path: Qwen/Qwen3-8Bstage: sftdataset: env_factory_sfttemplate: qwen3mask_history: Truecutoff_len: 16384per_device_train_batch_size: 1gradient_accumulation_steps: 32learning_rate: 1.0e-6num_train_epochs: 1warmup_ratio: 0.1bf16: truedeepspeed: examples/deepspeed/ds_z3_config.json。论文训练设置与 released SFT example 的 epoch 数不完全一致:paper 写 SFT 3 epochs 并从第 1 个 SFT epoch checkpoint 初始化 RL;repo 的示例 launch config 当前只保存 num_train_epochs: 1,更像复现实验中用于 RL 初始化的 SFT checkpoint 配置。

5. Experimental Results (实验结果)

Main results: SFT cold start 最大,RL after SFT 继续提升

主表显示 SFT 本身已经带来最大相对增益,RL 在 SFT checkpoint 上继续提升。关键数字如下(EnvFactory “Our” 表示 SFT+RL,总 tasks=2,575;“Our SFT” 表示只用 1,622 SFT tasks):

BackboneMethodEnv. / TasksBFCL SingleBFCL MultiMCP Pass / Cov. Avg.Vita Avg.Overall
Qwen3-1.7BBase— / —79.4816.751.03 / 6.2514.611.3316.27
Qwen3-1.7BOur (SFT)85 / 1,62278.3023.251.72 / 10.0515.576.3318.60
Qwen3-1.7BOur85 / 2,57578.4428.383.09 / 9.6415.117.3319.74
Qwen3-4BBase— / —85.1533.504.12 / 12.8625.257.6724.09
Qwen3-4BEnvScaler191 / 11,57283.6445.009.97 / 22.2729.2514.6929.56
Qwen3-4BOur (SFT)85 / 1,62285.1044.257.90 / 19.6625.2511.3327.29
Qwen3-4BOur85 / 2,57585.4648.509.97 / 21.8930.1316.0030.77
Qwen3-8BBase— / —84.3141.255.15 / 14.8632.3016.7029.23
Qwen3-8BEnvScaler191 / 11,57284.7451.889.62 / 22.6334.3018.6732.72
Qwen3-8BOur (SFT)85 / 1,62284.8346.508.25 / 22.8632.7116.6730.82
Qwen3-8BOur85 / 2,57586.0249.0013.75 / 25.9833.6718.6733.40

几个结论比较明确:Qwen3-4B 从 base 到 Our 的 BFCL multi-turn 从 33.50 到 48.50, Avg. 从 25.25 到 30.13,Vita Avg. 从 7.67 到 16.00;Qwen3-8B 的 MCP-Atlas Pass Rate 从 5.15 到 13.75,Mean Coverage 从 14.86 到 25.98。1.7B 上 BFCL multi-turn 从 16.75 到 28.38,但 BFCL single-turn 从 79.48 轻微降到 78.44,说明训练主要增强的是多轮/缺参/状态交互能力,而非所有 single-turn function calling 指标都同步上涨。

Environment scaling 与数据效率

Figure 3 解读:左图比较 50、75、85 个 environments 下 Qwen3-1.7B/4B/8B 的 BFCL-v3 multi-turn 平均表现,三种规模都随环境数增加而提升,但 50→75 的收益大于 75→85,呈现 diminishing returns。右图把环境数量、训练任务数量和 BFCL multi-turn 放在一起,显示 EnvFactory 用 85 个环境、2,575 个总任务就能达到强结果;对比 AWM 的 526 environments / 3,315 RL tasks 与 EnvScaler 的 191 environments / 9,022 SFT + 2,550 RL tasks,EnvFactory 的主要优势是 verified stateful environments 与 dependency-aware trajectories 的质量,而不是 brute-force 扩数量。

效率表的 exact numbers 是:AWM 使用 526 environments、3,315 RL tasks;EnvScaler 使用 191 environments、9,022 SFT tasks、2,550 RL tasks;EnvFactory 使用 85 environments、1,622 SFT tasks、953 RL tasks。

Ablations: direct RL、refinement、reward weighting

Direct RL 不经过 SFT warm-up 也能提升部分指标,但不如 SFT 后 RL 稳定:Our-4B (RL) 将 BFCL multi-turn 从 33.50 提到 41.38,VitaBench 从 7.67 提到 12.74;Our-1.7B (RL) 将 -Bench 从 14.67 提到 18.28;Our-8B (RL) 将 BFCL multi-turn 从 41.25 提到 44.35、VitaBench 从 16.70 到 17.00,但 -Bench 从 32.33 降到 29.08。作者据此认为 SFT initialization 对稳定 policy optimization 仍然重要。

Refinement ablation 用 250 条 SFT trajectories 比较 refined vs unrefined:1.7B BFCL overall 从 21.25 到 22.12;4B 从 40.88 到 41.25;8B 从 43.00 到 44.00。细项上,4B 的 Miss-Param 从 30.5 到 32.0,Long-Context 从 34.0 到 36.0;8B 的 Base 从 51.5 到 55.0、Miss-Param 从 38.5 到 39.0。这支持 calibrated refinement 对 ambiguous settings 有帮助,但提升幅度不是所有细项都一致(例如 8B Miss-Func 与 Long-Context 持平)。

Figure 4 解读:横轴是 trajectory reward 权重 ,固定 length penalty ;只依赖 state reward()或只依赖 trajectory matching()都会退化, 达到 BFCL-v3 peak accuracy 41.38%。这说明 tool-use reward 需要同时约束执行路径与最终状态:路径约束能避免“结果碰巧对但过程冗余/错误”,状态约束能容忍等价顺序或无关参数差异。

Limitations 与结论

作者明确的 limitation 是 MCP server 是 stateful 的,write-capable tools 会修改共享 database,因此每个 conversation 需要 dedicated transport connection 来避免 cross-contamination,这限制了大规模数据合成时的并行 tool invocation;论文通过 asynchronous synthesis pipeline 同时跑许多 isolated sessions 来缓解,但 per-connection bottleneck 仍存在。Broader impact 部分还指出,自动生成可执行工具生态可能被滥用于金融欺诈、phishing 等敏感领域模拟,也可能继承在线资源和 underlying models 的偏见,因此需要限制性 license、透明数据记录与安全约束。

总体上,EnvFactory 证明了一个重要方向:在 Tool-Use Agent 里,RL 的关键不只是 reward algorithm,而是能否批量构造可执行、可验证、有状态、依赖合理的环境和轨迹。实验显示,高质量 environment + topology-aware trajectory synthesis 可以用更小数据预算换来稳定 multi-turn tool-use 提升,尤其在 BFCL multi-turn、MCP-Atlas、VitaBench 这类需要状态和交互的 benchmark 上效果更明显。