Reading + AI

EnvScaler: Scaling Tool-Interactive Environments for LLM Agent via Programmatic Synthesis

Xiaoshuai Song, Haofei Chang, Guanting Dong, Yutao Zhu, Ji-Rong Wen, Zhicheng Dou

arXiv:2601.05808v2 · This workspace stays on this version. Check arXiv for newer versions

Affiliations and author notes
  • Gaoling School of Artificial Intelligence, Renmin University of China.
  • {songxiaoshuai,dou}@ruc.edu.cn
  • GitHub: https://github.com/RUC-NLPIR/EnvScaler
  • Corresponding Author
Some source details need review 23 to check

Original formulas, table values and figures are preserved. Comparison can miss differences; use the original PDF to check details.

Reading view checked: 105 math expressions, 36 rendering failures; 41 of 43 images available.

Compared 133 of 135 source passages against 24 of 32 PDF pages.

  • Some figure, table or graphic images are unavailable; the original paper is still accessible. (2)
  • Some graphics are laid out differently in the HTML source than in the paper (text or formulas outside their frame); check the original PDF. (16)
  • Some formulas could not be rendered; their original TeX is shown. Check the original PDF. (36)
  • A4_F12: the arXiv HTML draws text or formulas of this graphic outside its frame and it could not be corrected safely (no-declared-box); the image shows the HTML layout, which differs from the PDF.
  • A4_F13: the arXiv HTML draws text or formulas of this graphic outside its frame and it could not be corrected safely (no-declared-box); the image shows the HTML layout, which differs from the PDF.
  • A4_F14: the arXiv HTML draws text or formulas of this graphic outside its frame and it could not be corrected safely (no-declared-box); the image shows the HTML layout, which differs from the PDF.
  • A4_F15: the arXiv HTML draws text or formulas of this graphic outside its frame and it could not be corrected safely (no-declared-box); the image shows the HTML layout, which differs from the PDF.
  • A4_F16: the arXiv HTML draws text or formulas of this graphic outside its frame and it could not be corrected safely (no-declared-box); the image shows the HTML layout, which differs from the PDF.
  • A4_F17: the arXiv HTML draws text or formulas of this graphic outside its frame and it could not be corrected safely (no-declared-box); the image shows the HTML layout, which differs from the PDF.
  • A4_F18: the arXiv HTML draws text or formulas of this graphic outside its frame and it could not be corrected safely (no-declared-box); the image shows the HTML layout, which differs from the PDF.
  • A4_F19: the arXiv HTML draws text or formulas of this graphic outside its frame and it could not be corrected safely (no-declared-box); the image shows the HTML layout, which differs from the PDF.
  • A4_F20: the arXiv HTML draws text or formulas of this graphic outside its frame and it could not be corrected safely (no-declared-box); the image shows the HTML layout, which differs from the PDF.
  • A4_F21: the arXiv HTML draws text or formulas of this graphic outside its frame and it could not be corrected safely (no-declared-box); the image shows the HTML layout, which differs from the PDF.
  • A4_F22: the arXiv HTML draws text or formulas of this graphic outside its frame and it could not be corrected safely (no-declared-box); the image shows the HTML layout, which differs from the PDF.
  • A4_F23: the arXiv HTML draws text or formulas of this graphic outside its frame and it could not be corrected safely (no-declared-box); the image shows the HTML layout, which differs from the PDF.
  • A4_F24: the arXiv HTML draws text or formulas of this graphic outside its frame and it could not be corrected safely (no-declared-box); the image shows the HTML layout, which differs from the PDF.
  • A4_F25: the arXiv HTML draws text or formulas of this graphic outside its frame and it could not be corrected safely (no-declared-box); the image shows the HTML layout, which differs from the PDF.
  • A4_F26: the arXiv HTML draws text or formulas of this graphic outside its frame and it could not be corrected safely (no-declared-box); the image shows the HTML layout, which differs from the PDF.
  • A4_p2_pic1: blank, oversized or incomplete source rendering; capture unavailable.
  • A4_F27: the arXiv HTML draws text or formulas of this graphic outside its frame and it could not be corrected safely (too-small); the image shows the HTML layout, which differs from the PDF.
  • A4_F28: the arXiv HTML draws 1 text or formula block(s) of this graphic outside its frame; each was scaled into its frame (to 87% or more) without changing any text or formula. Text may look smaller than in the PDF.
  • A4_T14: blank, oversized or incomplete source rendering; capture unavailable.
  • This long paper was only partially compared; the original sources remain available.

41 of 43 figure, table and graphic images captured. Open original PDF

Abstract

Large language models (LLMs) are expected to be trained to act as agents in various real-world environments, but this process relies on rich and varied tool-interaction sandboxes. However, access to real systems is often restricted; LLM-simulated environments are prone to hallucinations and inconsistencies; and manually built sandboxes are hard to scale. In this paper, we propose EnvScaler, an automated framework for scalable tool-interaction environments via programmatic synthesis. EnvScaler comprises two components. First, SkelBuilder constructs diverse environment skeletons through topic mining, logic modeling, and quality evaluation. Then, ScenGenerator generates multiple task scenarios and rule-based trajectory validation functions for each environment. With EnvScaler, we synthesize 191 environments and about 7K scenarios, and apply them to Supervised Fine-Tuning (SFT) and Reinforcement Learning (RL) for Qwen3 series models. Results on three benchmarks show that EnvScaler significantly improves LLMs’ ability to solve tasks in complex environments involving multi-turn, multi-tool interactions.

Large language models (LLMs) are increasingly expected to serve as agents in a wide range of real-world applications, such as modifying orders in e-commerce backends, rescheduling flights via ticketing platforms, or managing documents in a file system (Luo et al., 2025; Yao et al., 2025; Qian et al., 2025). In these applications, the agent operates within a specific environment (Env), interacting with the user to gather information and invoking tools to query or update the Env’s state, as illustrated in Figure 1. This challenges LLMs to combine dialogue and tool use, adapt actions based on Env feedback, and solve tasks while respecting Env rules over long-horizon trajectories.

Figure 1
Figure 1: An illustration of tool-interactive environments. The environment (1) defines rules and provides tool interfaces to the agent; (2) executes the agent’s tool calls to update its state and return results.

To develop such capable LLM agents, scaling up rich and diverse tool-interactive environments is essential. Whether by collecting trajectories followed by imitation learning, or by autonomous exploration and reinforcement learning (RL) within Envs, we hope that exposure to a sufficiently broad range of environments during training will enable LLMs to generalize effectively to unseen environments and scenarios at test time (Huang et al., 2025; Liu et al., 2025a; Froger et al., 2026). However, as compared in Table 1, real-world environments often have restricted access; LLM-simulated environments also suffer from hallucinations and inconsistencies. Recently, a series of studies (Patil et al., 2025; Yao et al., 2025; Lu et al., 2025) build stateful, tool-interactive sandboxes through executable programs, offering advantages in controllability and stability. Nonetheless, these environments are manually crafted for evaluation purposes, with limited coverage and scalability. Therefore, a key challenge lies in automating the synthesis and scaling of sandbox environments to support training. It requires creating diverse, high-quality environments with states, tools, and interaction logic, and designing tasks that align with each environment.

Table 1
Env TypeScal- ableConsis- tentControl- lableSta- bleExplain- able
Real-World✗✓✗✓✓
LLM-Simulated✓✗✓✗✗
Programmatic✓✓✓✓✓
Table 1: Key property comparison of three Env types for LLM training. Scalable: ease of large-scale expansion; Consistent: logical coherence between multiple calls; Controllable: flexibility in modifying Env logic; Stable: reproducible over time; Explainable: transparency of Env logic. Symbols denote: ✓full support, ✗not supported, ✓partial or conditional support.
View original tableOriginal Table 1

Several studies have made progress in tackling this challenge, with LLMs used as programmers of environment logic rather than direct simulators. One approach (Ye et al., 2025; Sullivan et al., 2025) focuses solely on tool-layer modeling. It does not model the sandbox’s state, nor consider the interaction logic between tools and the database. Another approach (Tang et al., 2024; Piriyakulkij et al., 2025) seeks to programmatically reconstruct environments from existing observations (e.g., trajectories), but inevitably depends on access to pre-existing environments. Besides, AgentScaler (Fang et al., 2025) and AutoForge (Cai et al., 2025) rely on pre-collected toolsets or tool documentation, and lack an automated mechanism for assessing environment quality. Due to these limitations, a notable gap remains in automatically synthesizing and scaling tool-interactive environments without relying on environmental priors or toolsets. To bridge this gap, we propose EnvScaler, an automated, scalable framework for synthesizing diverse, executable, tool-interactive environments to train LLM agents, as shown in Figure 2.

We first introduce SkelBuilder to automate the construction of environmental skeletons, covering topic mining, logic modeling, and assessment. It comprises three modules: (1) Task-driven environment discovery: mines diverse environment themes from existing open-source task sets. (2) Executable environment construction: starting from an environment description, it plans states and tools, and programmatically implements them into a complete, runnable environment. (3) Quality inspection: a testing agent sends tool requests, while a checking agent assesses whether executions meet expectations. This process iterates over multiple rounds, with the pass rate indicating environment quality.

Figure 2
Figure 2: The overview of EnvScaler.

To further synthesize multiple task scenarios for each environment, we propose ScenGenerator. To ensure task relevance and solvability within a given environment and scenario, ScenGenerator first synthesize the environment’s initial database/state, and then derives challenging tasks from the current state. To achieve rule-based trajectory verification, ScenGenerator generates a set of terminal-state validation functions for each task. After the trajectory ends, these functions check whether the final environment state meets the expected conditions, using the functions’ pass rate as the reward score.

To validate the effectiveness of EnvScaler, we synthesized 191 environments and about 7K scenarios, applying them to SFT and RL for the Qwen3 series models. Evaluation on multiple tool-use benchmarks (Patil et al., 2025; Yao et al., 2025; Chen et al., 2025) shows that EnvScaler significantly enhances LLMs’ ability to solve tasks in complex environments involving multi-turn, multi-tool interactions. Further analysis of environment coverage, scale, and training strategies provides insights into how synthetic environments promote tool learning and generalization for LLM agents.

Figure 3
Figure 3: The overall framework of SkelBuilder.

In summary, we propose EnvScaler for scalable tool-interactive environment synthesis. Our contributions are threefold: (1) We propose SkelBuilder, an automated framework for synthesizing diverse, executable environment skeletons. (2) We propose ScenGenerator, a scenario generation pipeline that produces state data, challenging tasks, and rule-based trajectory verification for each environment. (3) Experiments on three benchmarks verify the effectiveness of EnvScaler in improving LLMs’ ability to solve tasks in complex environments involving multi-turn, multi-tool interactions.

Many studies aim to improve LLMs’ ability to solve tasks with tools (Qu et al., 2025; Luo et al., 2025). In this paper, we focus on general tool use across various domain-specific environments (Patil et al., 2025; Yao et al., 2025; Chen et al., 2025), rather than tool-integrated reasoning and web information access centered on Python or search tools (Dong et al., 2025; Li et al., 2025a). Some work have explored the training data and RL strategies from different perspectives (Prabhakar et al., 2025; Liu et al., 2025b; Xu et al., 2025; Zhang et al., 2026; Zhao et al., 2025). However, they mainly focus on synthetic static trajectories and cannot support LLMs’ self-exploration. For trajectory evaluation, they primarily rely on surface matching, checking whether generated tool names and parameters match references, which is neither sufficient to determine whether the task is truly completed nor able to accommodate multiple equivalent solution paths. In contrast, we synthesize executable environments and tasks, along with rule-based evaluation grounded in environments’ state, thereby supporting LLMs’ training across varied scenarios.

Environments provide agents with action feedback and rewards for interaction and policy optimization. We focus on tool-interactive environments, where LLM agents can use tools to query environmental information or change the state of the environment. One line of work (Guo et al., 2024; Guo et al., 2025; Castellani et al., 2025; Li et al., 2025b) leverages LLMs’ reasoning and world knowledge to simulate environments. Although there is no need to build real environments, it is prone to hallucinations and inconsistencies, and lacks transparency and persistent state management. Another line of work (Tang et al., 2024; Ye et al., 2025; Fang et al., 2025; Cai et al., 2025) builds sandbox environments through programming. However, they either only model isolated, stateless functions, or rely on environmental priors (e.g., trajectories, toolsets) and lack automatic assessment, which limits scalability and coverage. Therefore, we propose EnvScaler to enable automatic, scalable environment and scenario synthesis for agent training.

Overview. The goal of SkelBuilder is to construct environments {E}\{E\}, where each can be abstracted as a set of three elements: E={Fexec,Edoc,Σtool}E=\{F_{\text{exec}},E_{\text{doc}},\Sigma_{\text{tool}}\}.

  • •

    Executable program files FexecF_{\text{exec}}: Complete logic implementation of EE’s states, tools, and rules.

  • •

    Documentation EdocE_{\text{doc}}: Provides the agent with introductions or rules about EE.

  • •

    Tool interface set Σtool\Sigma_{\text{tool}}: Names, parameters, and descriptions of all tools exposed to the agent, serving as the entry for agent–Env interaction.

As shown in Figure 3, SkelBuilder enables an automated workflow from text resource mining to environment modeling and evaluation.

The first step in scaling environments is to collect diverse environment themes. Unlike manual presetting or derivation from API collections (Fang et al., 2025), SkelBuilder mines them from existing text resources. Considering that studies around SFT have gathered a large and diverse set of tasks that may implicitly contain latent environmental contexts, this inspired us to derive themes through reverse inference from the existing tasks.

Given a task set Texist={t1,…,tn}T_{\text{exist}}=\{t_{1},\dots,t_{n}\}, an LLM MM first performs binary filtering to retain tasks situated within a domain-specific, stateful environment. For each retained task, MM infers the corresponding environment description:

{Edes′}={M(Pinferenv∣∣t)∣t∈Texist,M(Pfiltertask∣∣t)},\{E^{\prime}_{\text{des}}\}=\{M(P^{\text{env}}_{\text{infer}}||t)\mid t\in T_{\text{exist}},M(P^{\text{task}}_{\text{filter}}||t)\},
(1)

where PfiltertaskP^{\text{task}}_{\text{filter}} and PinferenvP^{\text{env}}_{\text{infer}} denote prompts for task filtering and environment inference1^{1}. The inferred environments are then aggregated and deduplicated by embedding each description and retaining one record from groups of highly similar descriptions, yielding the final diverse, non-redundant set {Edes}=Dedup({Edes′},sim)\{E_{\text{des}}\}=\text{Dedup}(\{E^{\prime}_{\text{des}}\},\text{sim}).

To transform the environment description into a programmatically modeled environment, we design a three-stage pipeline.

Logic Planning. An LLM enriches the environment description EdesE_{\text{des}}, inferring the Env state definition EstateE_{\text{state}}, domain rules EruleE_{\text{rule}}, and the list of tool operations {Etooli}\{E_{\text{tool}_{i}}\}. These elements serve as a structured blueprint, with EruleE_{\text{rule}} concatenated with EdesE_{\text{des}} to form the environment documentation EdocE_{\text{doc}}:

Estate,Erule=M(Pplanstate∣∣Edes),{Etooli}=M(Pplantool∣∣Edes∣∣Estate∣∣Erule).\begin{aligned}\displaystyle E_{\text{state}},E_{\text{rule}} & \displaystyle=M(P^{\text{state}}_{\text{plan}}||E_{\text{des}}), \\ \displaystyle\{E_{\text{tool}_{i}}\} & \displaystyle=M(P^{\text{tool}}_{\text{plan}}||E_{\text{des}}||E_{\text{state}}||E_{\text{rule}}).\end{aligned}
(2)

Program Modeling. The LLM first converts the planned state space into class attribute definitions FattrF_{\text{attr}}. Then, for each tool operation, given the environment rules and class attributes, it generates the corresponding class-method implementation FmethiF_{\text{meth}_{i}}, ensuring consistency with rules and proper state transitions:

Fattr=M(Pexecstate∣∣ Estate),Fmethi=M(Pexectool∣∣ Erule∣∣ Fattr∣∣ Etooli).\begin{aligned}\displaystyle F_{\text{attr}} & \displaystyle=M(P^{\text{state}}_{\text{exec}}||\ E_{\text{state}}), \\ \displaystyle F_{\text{meth}_{i}} & \displaystyle=M(P^{\text{tool}}_{\text{exec}}||\ E_{\text{rule}}||\ F_{\text{attr}}||\ E_{\text{tool}_{i}}).\end{aligned}
(3)

Program Assembly. The generated code fragments are automatically merged into a complete Python class file FexecF_{\text{exec}}, implementing all sandbox logic, where attributes represent environment states and methods represent supported tool operations:

Fexec=Merge(Fattr,{Fmethi}i=1m).F_{\text{exec}}=\text{Merge}(F_{\text{attr}},\{F_{\text{meth}_{i}}\}^{m}_{i=1}).
(4)

Syntax validity is verified via the abstract syntax tree (AST), with invalid files discarded. AST combined with regex extraction yields all method signatures, forming the tool interface set Σtool\Sigma_{\text{tool}} 2^{2}.

Figure 4
Figure 4: The overall framework of ScenGenerator.

Unlike static evaluation via direct LLM scoring, we propose a dual-agent loop to assess the actual tool-execution performance of the environment.

Frontend Testing Agent. After instantiating the environment class, the testing LLM agent MtestM_{\text{test}} has no access to its internal implementation. In round jj, it receives the current environment state SjS_{j}, and randomly generates a call request, which may be a positive or a negative test case (e.g., invoking a file-deletion tool to delete a non-existent file):

callj=Mtest(Σtool,Sj).\text{call}_{j}=M_{\text{test}}(\Sigma_{\text{tool}},S_{j}).
(5)

Backend Checking Agent. The environment first executes the tool invocation. The checking agent McheckM_{\text{check}}, inspects the tool’s source code FmethF_{\text{meth}}, the returned result RjR_{j}, and state changes before and after execution to judge whether the behavior matches expectations:

Rj,Sj+1=Exec(Fmeth, callj, Sj),judgej=Mcheck(callj, Rj, ΔSj→j+1).\begin{aligned}\displaystyle R_{j},S_{j+1} & \displaystyle=\text{Exec}(F_{\text{meth}},\ \text{call}_{j},\ S_{j}), \\ \displaystyle\text{judge}_{j} & \displaystyle=M_{\text{check}}(\text{call}_{j},\ R_{j},\ \Delta S_{j\rightarrow j+1}).\end{aligned}
(6)

The testing and checking agents form a closed loop, iterating for NN rounds to cover diverse situations. The average judging pass rate serves as the quantitative metric for the environment’s quality:

scoreenv=1N∑j=1Njudgej,\text{score}_{\text{env}}=\frac{1}{N}\sum_{j=1}^{N}\text{judge}_{j},
(7)

where environments with scoreenv\text{score}_{\text{env}} below a predefined threshold are discarded.

In practice, we select API-Bank (Li et al., 2023) and ToolACE (Liu et al., 2025b) as initial task sources, which have high task retention rates. GPT-4.1 and Qwen3-235B-Instruct-2507 are used for environment discovery and programming, while GPT-4.1-mini and Qwen3-30B-A3B-Instruct-2507 are used for environment assessment. The number of test rounds NN is set to 100, with a filtering threshold of 0.85. In total, we obtain 191 environments. Table 2 and Figure 10 presents their statistics, with an average of 18.58 tools and 21.38 state categories per environment, reflecting the complexity of the synthesized environments.

Table 2
ItemAvg.Med.
# Constraint Rules Per Env4.585
# State Category Per Env
Level 1 (e.g., user, message, item)3.744
Level 2 per Level 1 (e.g., u_id, u_phone)5.725
Total21.3821
# Tools Per Env
Env Information Query (e.g., list_users)10.4410
Env State Change (e.g., send_message)8.148
Total18.5818
Table 2: Statistics across 191 synthetic environments.
View original tableOriginal Table 2

Overview. The skeleton of the environment alone is insufficient to support agent interactions. An environment also requires initial state data, tasks, and an evaluation mechanism for assessing agents’ action trajectories. As shown in Figure 4, we propose ScenGenerator to automatically construct multiple task scenarios for each environment.

In stateful environments, tasks are closely tied to state data. For example, an agent cannot cancel an order that does not exist in the environment’s database. To ensure that the generated task is both solvable and consistent with the given environment and scenario, ScenGenerator first uses an LLM to generate the environment’s initial state data SinitS_{\text{init}}, and then derives challenging tasks based on the environment’s initial state:

Sinit=M(Pinitgen∣∣Fexec∣∣Estate).S_{\text{init}}=M(P_{\text{init}}^{\text{gen}}||F_{\text{exec}}||E_{\text{state}}).
(8)

A common task synthesis approach (Fang et al., 2025; Cai et al., 2025) is to walk through tool invocation sequences and infer the corresponding task by reversing the sequence. However, this often yields low-quality tasks, and the existence of multiple valid task solution paths also make the sequence unsuitable as a unique “ground-truth” reference. Therefore, we directly prompt the LLM to generate a challenging, scenario-specific task from the initial state S0S_{0}, tool set EtoolE_{\text{tool}}, and rules EruleE_{\text{rule}}:

task=M(Ptaskgen∣∣Sinit∣∣Etool∣∣Erule).\text{task}=M(P_{\text{task}}^{\text{gen}}||S_{\text{init}}||E_{\text{tool}}||E_{\text{rule}}).
(9)

To achieve rule-based trajectory verification, we first use an LLM to decompose the task into a checklist of verifiable conditions. Then, for each checkpoint, we call the LLM to generate a terminal-state validation function. This function takes the environment’s final state SfinalS_{\text{final}} after the trajectory as input and returns “True” or “False” to indicate whether the condition is satisfied. Finally, the proportion of passed functions is used as the trajectory’s reward score. Formally, we have:

{ck}k=1K=M(Plistcheck∣∣task),fck=M(Pfunccheck∣∣ck),reward=1K∑k=1K1[fck(Sfinal)=True].\begin{aligned}\displaystyle\{c_{k}\}_{k=1}^{K} & \displaystyle=M(P^{\text{check}}_{\text{list}}||\text{task}), \\ \displaystyle f_{c_{k}} & \displaystyle=M(P^{\text{check}}_{\text{func}}||c_{k}), \\ \displaystyle\text{reward} & \displaystyle=\frac{1}{K}\sum_{k=1}^{K}\mathbf{1}\left[f_{c_{k}}(S_{\text{final}})=\text{True}\right].\end{aligned}
(10)

Compared with a single boolean judgment, decomposing the task into multiple validation functions not only captures partial completion, but also is easier for the LLM to generate. Moreover, unlike superficial evaluation that merely checks whether the tool invocation sequence matches a reference sequence, evaluation based on the environment’s final state is agnostic to the solution process and can accommodate multiple valid solution paths.

Table 3
Qwen3 -4B(NT)Qwen3 -4BQwen3 -8BQwen3-8B (max@8)Qwen3 -30B
Non-Conversation
Score37.5353.0557.7869.3867.07
Avg Step21.3612.9613.66-16.78
Conversation
Score46.1355.5658.3574.4863.08
Avg Step27.2025.4825.90-25.38
Table 3: Average scores and step counts of LLMs on a random sample of 50 scenarios. Qwen3-30B is Qwen3-30B-A3B-Thinking-2307. NT: Non-Think.
View original tableOriginal Table 3

The agent–environment interaction can be modeled as a Partially Observable Markov Decision Process (POMDP). At time step tt, the agent πθ\pi_{\theta} cannot directly access the environment state data StS_{t}; instead, it makes decisions based on the observation–action history HtH_{t} and the current observation oto_{t}:

at=πθ(Ht,ot)ot+1, St+1=E(at,St).a_{t}=\pi_{\theta}(H_{t},o_{t})\quad o_{t+1},\,S_{t+1}=E(a_{t},S_{t}).
(11)

We consider two interaction settings:3^{3}

(1) Non-Conversation (Non-Conv.): The environment directly provides the complete task information to the agent, with the initial observation o0={Edoc,Σtool,task}o_{0}=\{E_{\text{doc}},\Sigma_{\text{tool}},\text{task}\}. At each step, the agent invokes a tool, and the execution result as the observation, until the agent believes the task is completed or the maximum step limit is reached.

(2) Conversation (Conv.): The environment additionally includes an LLM-simulated user πuser\pi_{\text{user}}. Complete task information is not revealed upfront but must be progressively acquired via agent-user dialogue. Compared with Non-Conv, it expands the agent’s action space to include user interaction. The trajectory ends when the user considers the task completed or the maximum steps are reached.

For SFT, trajectories from the teacher LLM can be directly used as learning targets for the student LLM. For RL, trajectories are converted into rewards using the validation functions, which are then applied for policy optimization.

With ScenGenerator, we construct around 7K task scenarios for 191 environments and randomly sample 50 scenarios for a pilot study. As shown in Table 3, Qwen3-8B (Thinking) scores 57.78, while Qwen3-4B (Non-Think) scores only 37.53. Besides, the average trajectory is about 15 steps under the Non-Conv setting, increasing to over 25 steps under Conv. This shows that the synthetic tasks have substantial difficulty and can incentivize LLMs to produce long action trajectories. Moreover, for the same model, multiple sampling and selecting the highest-scoring trajectory significantly boost the score, suggesting that synthetic tasks offer considerable room for self-exploration and optimization. Figure 5 shows that stronger LLMs consistently achieve higher win rates, confirming the validity of the state-check functions in distinguishing and quantifying model performance.

Figure 5
Figure 5: Pairwise comparison of different LLMs on a random sample of 50 scenarios under Non-Conv setting.
Table 4
ModelBFCL-v3 Multi-TurnTau-BenchACEBench-Agent
BaseMiss- FuncMiss- ParamLong- ContextOverallRetailAirlineOverallMulti- StepMulti- TurnOverall
Advanced Models
GPT-4.146.0037.5032.0044.5040.0066.9548.057.4895.0060.0077.50
Qwen3-235B-Thinking-250760.0035.0034.0054.0045.7567.846.0056.9085.0063.3374.17
Qwen3-235B-Instruct-250758.0033.0027.5050.5042.2571.3044.0057.6580.0063.3371.67
Kimi-K2-Instruct-090557.5035.0042.0049.0045.8869.8754.0061.9485.0073.3379.17
Qwen3-1.7B (Thinking)13.506.0012.507.009.758.9916.0012.5035.028.8931.95
+ EnvScaler (w/ SFT)24.5011.5020.0016.50\text{18.13}_{{\color[rgb]{0,0.2734,0}\text{+8.38}}}20.8714.00\text{17.44}_{{\color[rgb]{0,0.2734,0}\text{+4.94}}}55.0032.22\text{43.61}_{{\color[rgb]{0,0.2734,0}\text{+11.66}}}
+ EnvScaler (w/ SFT&RL)31.5017.0020.5023.00\text{23.00}_{{\color[rgb]{0,0.2734,0}\text{+13.25}}}18.5514.00\text{16.28}_{{\color[rgb]{0,0.2734,0}\text{+3.78}}}66.6733.33\text{50.00}_{{\color[rgb]{0,0.2734,0}\text{+18.05}}}
Qwen3-4B (Thinking)32.0020.0024.0025.5025.3840.8726.0033.4458.3352.2255.28
+ EnvScaler (w/ SFT)47.0024.0031.5037.00\text{34.88}_{{\color[rgb]{0,0.2734,0}\text{+9.50}}}44.3532.00\text{38.20}_{{\color[rgb]{0,0.2734,0}\text{+4.76}}}73.3360.00\text{66.67}_{{\color[rgb]{0,0.2734,0}\text{+11.39}}}
+ EnvScaler (w/ SFT&RL)51.0034.0028.0039.00\text{38.00}_{{\color[rgb]{0,0.2734,0}\text{+12.62}}}48.1234.00\text{41.06}_{{\color[rgb]{0,0.2734,0}\text{+7.62}}}80.0061.11\text{70.55}_{{\color[rgb]{0,0.2734,0}\text{+15.27}}}
Qwen3-8B (Thinking)32.0033.5022.0028.0028.8846.3830.0038.1963.3356.6760.00
+ EnvScaler (w/ SFT)47.0033.0029.5038.50\text{37.00}_{{\color[rgb]{0,0.2734,0}\text{+8.12}}}48.7034.00\text{41.35}_{{\color[rgb]{0,0.2734,0}\text{+3.16}}}83.3360.00\text{71.67}_{{\color[rgb]{0,0.2734,0}\text{+11.67}}}
+ EnvScaler (w/ SFT&RL)55.5036.0035.0041.00\text{41.88}_{{\color[rgb]{0,0.2734,0}\text{+13.00}}}53.6236.00\text{44.81}_{{\color[rgb]{0,0.2734,0}\text{+6.62}}}85.0060.00\text{72.50}_{{\color[rgb]{0,0.2734,0}\text{+12.50}}}
Table 4: Performance comparison of models on three benchmarks. We bold the Overall results for each benchmark.
View original tableOriginal Table 4

Training. We conduct SFT and RL on Qwen3 series models (Thinking Mode) (Yang et al., 2025). A total of 140 environments are used for SFT, and the remaining 51 environments are used for RL. For SFT, we employ Qwen3-30B-A3B-Thinking-2507 as the teacher model, interacting with the environments under two interaction settings, yielding about 9K trajectories in total. For RL, we apply the Reinforce++ algorithm (Hu et al., 2025) under Non-Conv Setting. Detailed implementation are provided in Appendix B.2.
Evaluation. We use three widely used multi-turn tool-use benchmarks: BFCL-v3 Multi-Turn (abbreviated as BFCL-MT) (Patil et al., 2025), Tau-Bench (Yao et al., 2025), and ACEBench-Agent (Chen et al., 2025). Each benchmark consists of several domain-specific environments equipped with tools, requiring the LLM to interact with users and invoke tools to solve tasks. Notably, ACEBench uses the [func_name(param)] prompt format by default; we modify the official code to support LLMs’ native function-calling interface to ensure consistency. Detailed introductions are provided in Appendix B.1 and Table 9.

As shown in Table 4, using EnvScaler for SFT leads to significant improvements across all benchmarks, and incorporating RL further boosts performance 4^{4}. Next, we analyze the results from three aspects:

(1) Training Strategy. SFT significantly boosts LLMs’ performance. Averaged across three models, BFCL-MT improves by 8.67 points, Tau-Bench by 4.29 points, and ACEBench-Agent by 11.57 points. This can be attributed to the diverse, multi-tool environments and complex tasks synthesized by EnvScaler, which help LLMs strengthen domain adaptability, multi-turn interaction, and multi-tool usage in supervised training. Incorporating RL yields further improvements (the RL curve is shown in Figure 6). For example, Qwen3-8B achieves 4.88 and 3.46 point gains on BFCL-MT and Tau-Bench, respectively. This demonstrates that leveraging synthetic environments and state-checking reward signals can guide LLMs to autonomously learn more effective strategies.

Figure 6
Figure 6: The RL training and validation curve of Qwen3 in synthetic environments after SFT.

(2) Model Size. SFT delivers stable improvements across models, whereas RL performance is more model-dependent. Qwen3-8B achieves gains across all datasets, whereas Qwen3-1.7B shows notable improvements on BFCL-MT and ACEBench-Agent but a slight drop on Tau-Bench. The main reason is that large-scale models possess stronger exploration capabilities during RL, enabling them to extract effective strategies. In contrast, small-scale models, with weaker foundational abilities, are more susceptible to noisy reward signals and produce lower-quality strategies that struggle to generalize to unseen environments.

(3) Different Benchmarks. Comparatively, EnvScaler shows more substantial gains on BFCL-MT and ACEBench-Agent, while improvements on Tau-Bench are relatively limited. Qwen3-1.7B even slightly declines on the most challenging Tau-Bench Airline task. This is mainly because BFCL-MT and ACEBench-Agent underscore the evaluation of LLM’s ability in multi-turn, multi-tool collaboration and domain adaptability, while EnvScaler can substantially enhance these capabilities. In contrast, Tau-Bench focuses on deep reasoning under complex environment’s rules, making such tasks harder for LLMs to learn and generalize.

Table 5
BaseMiss- FuncMiss- ParmLong- Cont.Over- all
Qwen3-4B32.0020.0024.0025.5025.38
+ SFT (Full)47.0024.0031.5037.0034.88
+ SFT (top 50%)40.5028.0027.5032.0032.00
+ SFT (bottom 50%)43.0027.5027.5032.0032.50
+ SFT (random 50%)42.0026.0026.0031.0031.25
Table 5: Qwen3-4B’s Performance on BFCL-MT with training on different environment-similarity subsets.
View original tableOriginal Table 5
Figure 7
Figure 7: The change of Qwen3-4B’s performance with the scaling of the number of environments for SFT.

To investigate how the similarity between synthetic and test environments affects test performance, we compute the similarity between each synthetic environment and those in BFCL-MT, based on textual embeddings of the environment topic and toolset descriptions. According to the similarity scores, environments are divided into three groups: (1) the 50% most similar; (2) the 50% least similar; and (3) a random 50% selection. As shown in Table 5, training on either the most similar or least similar subsets yields only minor differences in overall metrics, and both significantly outperform the baseline. This suggests that the performance gains do not primarily depend on direct similarity between training and test environments, but on the problem-solving patterns and transferable tool-use skills learned from different environments.

Table 6
BaseMiss FuncMiss ParmLong Cont.Over- all
Qwen3-8B32.0033.5022.0028.0028.88
+ SFT (Non-Conv)49.5032.5023.5037.5035.75
+ SFT (Conv)45.5032.0030.5034.0035.50
+ SFT (Full)47.0033.0029.5038.5037.00
Table 6: Qwen3-8B’s Performance on BFCL-MT with training on different subsets of interaction patterns.
View original tableOriginal Table 6

To analyze the effect of scaling the number of training environments on model performance, we sample subsets with varying environment counts from the full SFT dataset and conduct SFT on each subset. As shown in Figure 7, Qwen3-4B’s scores on both the BFCL-MT and ACEBench-Agent exhibit a steady upward trend as the number of training environments increases. The most pronounced improvement occurs when scaling from 0 to 20 environments; although the rate of gain slows beyond this point, performance continues to rise overall. These results indicate that scaling up the number and diversity of training environments effectively enhances the model’s adaptability across contexts and improves its task-solving performance.

Table 7
Model (Thinking)BFCL Multi-TurnACEBench -AgentTau- Bench
Qwen3-1.7B9.7512.5031.95
+ EnvScaler (RL)\text{12.12}_{{\color[rgb]{0,0.2734,0}\text{+2.37}}}\text{13.08}_{{\color[rgb]{0,0.2734,0}\text{+0.58}}}\text{32.22}_{{\color[rgb]{0,0.2734,0}\text{+0.27}}}
Qwen3-4B25.3833.4455.28
+ EnvScaler (RL)\text{29.38}_{{\color[rgb]{0,0.2734,0}\text{+4.00}}}\text{35.15}_{{\color[rgb]{0,0.2734,0}\text{+1.71}}}\text{61.25}_{{\color[rgb]{0,0.2734,0}\text{+5.97}}}
Qwen3-8B28.8838.1960.00
+ EnvScaler (RL)\text{37.62}_{{\color[rgb]{0,0.2734,0}\text{+8.74}}}\text{42.30}_{{\color[rgb]{0,0.2734,0}\text{+4.11}}}\text{63.05}_{{\color[rgb]{0,0.2734,0}\text{+3.05}}}
Table 7: Performance of models trained with direct RL.
View original tableOriginal Table 7
Figure 8
Figure 8: The Direct RL training and validation curve of Qwen3 in synthetic environments without SFT.

To investigate the impact of interaction patterns on model training, we conduct SFT under three data settings: Non-Conv, Conv, and their combination (Full). Table 6 reports the BFCL-MT results for Qwen3-8B. The Base and Long-Context subsets have complete task information, whereas the Miss-Func and Miss-Parm subsets lack necessary tools or parameters, requiring the agent to interact with the user to obtain them. It can be observed that SFT (Non-Conv) performs better on Base and Long-Context, while SFT (Conv) achieves higher scores on Miss-Parm. Performance on Miss-Func does not improve, primarily because the training data lacks samples with missing tool types. The SFT (Full) setting yields the highest overall score, highlighting the need to learn both interaction patterns for adaptability in scenarios with either sufficient or incomplete information.

Table 7 shows the performance of models trained with RL directly on synthetic environments without SFT cold start. Figure 8 presents the curves during the RL process. Overall, all models achieve varying degrees of improvement on three benchmarks, indicating that even without SFT initialization, direct RL can still drive self-optimization. However, the gains from direct RL strongly depend on model size and exploration capability. Qwen3-1.7B shows only minor improvement, while Qwen3-8B achieves the most notable gains. This suggests that larger models can leverage the environment more effectively for exploration and learning high-quality policies, whereas smaller models, limited in base ability and exploration, are more vulnerable to sparse or noisy rewards. Besides, compared with “SFT + RL” in Table 4, the overall gains from direct RL remain limited, highlighting that SFT initialization is still important for better policy quality. Combining SFT with RL can more fully exploit the training value of synthetic environments.

Figure 9
Figure 9: The score distribution of environment quality.

To further evaluate the quality of environments, we decompose the quality evaluation into three dimensions and find that Claude-4.5-Sonnet’s scores align highly with manual judgments: (1) Tool-Program Alignment: it evaluates whether the semantic definition of the tool is consistent with its program implementation. (2) Functional Correctness: it evaluates whether the tool behaviors in the environment meet expectations. (3) Code Robustness: it evaluates whether the environment program handles abnormal inputs and boundary conditions reasonably. As shown in Figure 9, the 191 synthesized environments achieve high average scores of 8.5+ across all three dimensions. This proves the overall reliability of the EnvScaler evaluation mechanism.

Table 8
StageTokensCost ($)
InputOutputTotal
Per Environment (SkelBuilder)
Discovery (GPT-4.1)152956920980.0076
Construction (GPT-4.1)5763916655742940.2485
Accessment (GPT-4.1-mini)17091595270617618650.7679
Total17683276993018382571.0240
Per Scenario (ScenGenerator)
Gen Database (GPT-4.1)5658220578630.0289
Gen Task (GPT-4.1)288355534380.0102
Check Func (GPT-4.1-mini)268798571354500.0244
Total3542011331467510.0635
Table 8: Average token usage and cost of EnvScaler.
View original tableOriginal Table 8

We evaluate the average token usage and cost of synthesizing a single environment and a single scenario on a small batch. The results are shown in Table 8. Under the GPT-4.1/mini combination, the average cost per environment is about $1 and the cost per scenario is about $0.06. We consider this acceptable. During environment construction, the 100-round dual-agent loop causes high token consumption. If the number of loop rounds is reduced, the cost can be further lowered.

In this paper, we propose EnvScaler, a scalable framework for synthesizing diverse, executable tool-interactive environments to train LLM agents. EnvScaler first constructs environment skeletons through topic mining, programmatic modeling, and dual-agent assessment (SkelBuilder), then synthesizes scenarios by generating initial states, challenging tasks, and rule-based trajectory verification (ScenGenerator). Experiments on three benchmarks demonstrate that EnvScaler significantly enhances LLMs’ ability to solve tasks through multi-turn, multi-tool interactions across environments.

In this paper, we propose EnvScaler for automated synthesis of tool-interactive environments. However, there are still some limitations, as follows: (1) Although LLMs are not directly used as environment simulators, the entire construction process relies on LLM synthesis, which may introduce biases compared to real systems (e.g., business logic, state definitions, and task design). (2) In terms of domain coverage, EnvScaler mainly targets domain-specific, stateful environments, with limited support for open environments such as web search or information access. (3) In terms of feature modeling, EnvScaler focuses on tool–state interactions but lacks explicit simulation of real-system characteristics such as interface latency, network fluctuations, and error patterns. (4) In terms of modality, EnvScaler only supports text-based tool inputs and outputs, without incorporating multimodal tools involving images, audio, or other modalities. (5) In terms of scale, although EnvScaler is highly scalable, the current study is limited to around 200 synthesized environments and 7K scenarios due to experimental resources.

This work was supported by National Natural Science Foundation of China No. 62272467 and No. 625B2178. The work was partially done at the Beijing Key Laboratory of Research on Large Models and Intelligent Governance.

References

  1. Cai et al. (2025) S. Cai, R. Fang, J. Wu, B. Li, X. Wang, Y. Jiang, L. Su, L. Zhang, W. Yin, Z. Zhang, F. Feng, P. Xie, and X. Wang AutoForge: automated environment synthesis for agentic reinforcement learning. External Links: 2512.22857, Link Cited by: §1, §2.2, §4.1.Source
  2. Castellani et al. (2025) T. Castellani, N. Ye, D. Mittal, T. Yen, and H. Namkoong SynthTools: a framework for scaling synthetic tools for agent development. External Links: 2511.09572, Link Cited by: §2.2.Source
  3. Chen et al. (2025) C. Chen, X. Hao, W. Liu, X. Huang, X. Zeng, S. Yu, D. Li, Y. Huang, X. Liu, W. Xinzhi, and W. Liu ACEBench: a comprehensive evaluation of LLM tool usage. In Findings of the Association for Computational Linguistics: EMNLP 2025, C. Christodoulopoulos, T. Chakraborty, C. Rose, and V. Peng (Eds.), Suzhou, China, pp. 12970–12998. External Links: Link, Document, ISBN 979-8-89176-335-7 Cited by: 3rd item, §1, §2.1, §5.1.SourceSource
  4. Dong et al. (2025) G. Dong, Y. Chen, X. Li, J. Jin, H. Qian, Y. Zhu, H. Mao, G. Zhou, Z. Dou, and J. Wen Tool-star: empowering llm-brained multi-tool reasoner via reinforcement learning. CoRR abs/2505.16410. External Links: Link, Document, 2505.16410 Cited by: §2.1.SourceSource
  5. Fang et al. (2025) R. Fang, S. Cai, B. Li, J. Wu, G. Li, W. Yin, X. Wang, X. Wang, L. Su, Z. Zhang, et al. Towards general agentic intelligence via environment scaling. arXiv preprint arXiv:2509.13311. Cited by: §1, §2.2, §3.1, §4.1.
  6. Froger et al. (2026) R. Froger, P. Andrews, M. Bettini, A. Budhiraja, R. S. Cabral, V. Do, E. Garreau, J. Gaya, H. Laurençon, M. Lecanu, K. Malkan, D. Mekala, P. Menard, G. M. Bertran, U. Piterbarg, M. Plekhanov, M. Rita, A. Rusakov, V. Vorotilov, M. Wang, I. Yu, A. Benhalloum, G. Mialon, and T. Scialom Gaia2: benchmarking LLM agents on dynamic and asynchronous environments. In The Fourteenth International Conference on Learning Representations, External Links: Link Cited by: §1.Source
  7. Guo et al. (2025) S. Guo, O. D. Domingues, R. Avalos, A. Courville, and F. Strub World modelling improves language model agents. External Links: 2506.02918, Link Cited by: §2.2.Source
  8. Guo et al. (2024) Z. Guo, S. Cheng, H. Wang, S. Liang, Y. Qin, P. Li, Z. Liu, M. Sun, and Y. Liu StableToolBench: towards stable large-scale benchmarking on tool learning of large language models. In Findings of the Association for Computational Linguistics: ACL 2024, L. Ku, A. Martins, and V. Srikumar (Eds.), Bangkok, Thailand, pp. 11143–11156. External Links: Link, Document Cited by: §2.2.SourceSource
  9. Hu et al. (2025) J. Hu, J. K. Liu, H. Xu, and W. Shen REINFORCE++: stabilizing critic-free policy optimization with global advantage normalization. External Links: 2501.03262, Link Cited by: 2nd item, §5.1.Source
  10. Huang et al. (2025) Y. Huang, S. Li, Z. Fan, M. LIU, W. Liu, and Y. R. Fung Scaling environments for LLM agents: fundamentals, approaches, and future directions. In Workshop on Scaling Environments for Agents, External Links: Link Cited by: §1.Source
  11. Li et al. (2023) M. Li, Y. Zhao, B. Yu, F. Song, H. Li, H. Yu, Z. Li, F. Huang, and Y. Li API-bank: a comprehensive benchmark for tool-augmented LLMs. In Proceedings of the 2023 Conference on Empirical Methods in Natural Language Processing, H. Bouamor, J. Pino, and K. Bali (Eds.), Singapore, pp. 3102–3116. External Links: Link, Document Cited by: §3.4.SourceSource
  12. Li et al. (2025a) X. Li, J. Jin, G. Dong, H. Qian, Y. Wu, J. Wen, Y. Zhu, and Z. Dou WebThinker: empowering large reasoning models with deep research capability. In The Thirty-ninth Annual Conference on Neural Information Processing Systems, External Links: Link Cited by: §2.1.Source
  13. Li et al. (2025b) Y. Li, H. A. Inan, X. Yue, W. Chen, L. Wutschitz, J. Kulkarni, R. Poovendran, R. Sim, and S. Rajmohan Simulating environments with reasoning models for agent training. External Links: 2511.01824, Link Cited by: §2.2.Source
  14. Liu et al. (2025a) A. Liu, A. Mei, B. Lin, B. Xue, B. Wang, B. Xu, B. Wu, B. Zhang, C. Lin, C. Dong, et al. Deepseek-v3. 2: pushing the frontier of open large language models. arXiv preprint arXiv:2512.02556. Cited by: §1.
  15. Liu et al. (2025b) W. Liu, X. Huang, X. Zeng, xinlong hao, S. Yu, D. Li, S. Wang, W. Gan, Z. Liu, Y. Yu, Z. WANG, Y. Wang, W. Ning, Y. Hou, B. Wang, C. Wu, W. Xinzhi, Y. Liu, Y. Wang, D. Tang, D. Tu, L. Shang, X. Jiang, R. Tang, D. Lian, Q. Liu, and E. Chen ToolACE: winning the points of LLM function calling. In The Thirteenth International Conference on Learning Representations, External Links: Link Cited by: §2.1, §3.4.Source
  16. Liu et al. (2024) Z. Liu, T. Hoang, J. Zhang, M. Zhu, T. Lan, S. Kokane, J. Tan, W. Yao, Z. Liu, Y. Feng, et al. APIGen: automated pipeline for generating verifiable and diverse function-calling datasets. In Proceedings of the 38th International Conference on Neural Information Processing Systems, pp. 54463–54482. Cited by: §C.1.
  17. Lu et al. (2025) J. Lu, T. Holleis, Y. Zhang, B. Aumayer, F. Nan, H. Bai, S. Ma, S. Ma, M. Li, G. Yin, Z. Wang, and R. Pang ToolSandbox: a stateful, conversational, interactive evaluation benchmark for LLM tool use capabilities. In Findings of the Association for Computational Linguistics: NAACL 2025, L. Chiruzzo, A. Ritter, and L. Wang (Eds.), Albuquerque, New Mexico, pp. 1160–1183. External Links: Link, Document, ISBN 979-8-89176-195-7 Cited by: §1.SourceSource
  18. Luo et al. (2025) J. Luo, W. Zhang, Y. Yuan, Y. Zhao, J. Yang, Y. Gu, B. Wu, B. Chen, Z. Qiao, Q. Long, et al. Large language model agent: a survey on methodology, applications and challenges. arXiv preprint arXiv:2503.21460. Cited by: §1, §2.1.
  19. Patil et al. (2025) S. G. Patil, H. Mao, F. Yan, C. C. Ji, V. Suresh, I. Stoica, and J. E. Gonzalez The berkeley function calling leaderboard (BFCL): from tool use to agentic evaluation of large language models. In Forty-second International Conference on Machine Learning, External Links: Link Cited by: 1st item, §1, §1, §2.1, §5.1.Source
  20. Piriyakulkij et al. (2025) W. T. Piriyakulkij, Y. Liang, H. Tang, A. Weller, M. Kryven, and K. Ellis PoE-world: compositional world modeling with products of programmatic experts. In The Thirty-ninth Annual Conference on Neural Information Processing Systems, External Links: Link Cited by: §1.Source
  21. Prabhakar et al. (2025) A. Prabhakar, Z. Liu, M. Zhu, J. Zhang, T. Awalgaonkar, S. Wang, Z. Liu, H. Chen, T. Hoang, J. C. Niebles, et al. Apigen-mt: agentic pipeline for multi-turn data generation via simulated agent-human interplay. arXiv preprint arXiv:2504.03601. Cited by: §C.1, §2.1.
  22. Qian et al. (2025) C. Qian, Z. Liu, A. Prabhakar, Z. Liu, J. Zhang, H. Chen, H. Ji, W. Yao, S. Heinecke, S. Savarese, and H. Wang UserBench: an interactive gym environment for user-centric agents. In Workshop on Scaling Environments for Agents, External Links: Link Cited by: §1.Source
  23. Qu et al. (2025) C. Qu, S. Dai, X. Wei, H. Cai, S. Wang, D. Yin, J. Xu, and J. Wen Tool learning with large language models: a survey. Frontiers of Computer Science 19 (8), pp. 198343. Cited by: §2.1.
  24. Sullivan et al. (2025) M. Sullivan, M. Hartmann, and A. Koller Procedural environment generation for tool-use agents. In Proceedings of the 2025 Conference on Empirical Methods in Natural Language Processing, C. Christodoulopoulos, T. Chakraborty, C. Rose, and V. Peng (Eds.), Suzhou, China, pp. 18555–18573. External Links: Link, Document, ISBN 979-8-89176-332-6 Cited by: §1.SourceSource
  25. Tang et al. (2024) H. Tang, D. Key, and K. Ellis Worldcoder, a model-based llm agent: building world models by writing code and interacting with the environment. Advances in Neural Information Processing Systems 37, pp. 70148–70212. Cited by: §1, §2.2.
  26. Wang et al. (2025) W. Wang, S. Xiong, G. Chen, W. Gao, S. Guo, Y. He, J. Huang, J. Liu, Z. Li, X. Li, et al. Reinforcement learning optimization for large-scale learning: an efficient and user-friendly scaling library. arXiv preprint arXiv:2506.06122. Cited by: 2nd item.
  27. Xu et al. (2025) Z. Xu, B. Hao, Z. Wang, Y. Wen, X. Xu, Y. Liu, L. Chen, D. Wang, M. Wang, T. Zhao, Y. Chen, C. Peng, J. Gu, L. Gan, X. Zhao, C. Zhuang, and S. Gu FunReason-mt technical report: advanced data synthesis solution for real-world multi-turn tool-use. External Links: 2510.24645, Link Cited by: §2.1.Source
  28. Yang et al. (2025) A. Yang, A. Li, B. Yang, B. Zhang, B. Hui, B. Zheng, B. Yu, C. Gao, C. Huang, C. Lv, et al. Qwen3 technical report. arXiv preprint arXiv:2505.09388. Cited by: §5.1.
  29. Yao et al. (2025) S. Yao, N. Shinn, P. Razavi, and K. R. Narasimhan {\$\tau\$}-bench: a benchmark for \underline{t}ool-\underline{a}gent-\underline{u}ser interaction in real-world domains. In The Thirteenth International Conference on Learning Representations, External Links: Link Cited by: 2nd item, §1, §1, §1, §2.1, §5.1.Source
  30. Ye et al. (2025) J. Ye, C. Jiang, Z. Du, Y. Xu, X. Yao, Z. Xi, X. Fan, Q. Zhang, T. Gui, X. Huang, et al. Feedback-driven tool-use improvements in large language models via automated build environments. arXiv preprint arXiv:2508.08791. Cited by: §1, §2.2.
  31. Zhang et al. (2026) S. Zhang, Y. Dong, J. Zhang, J. Kautz, B. Catanzaro, A. Tao, Q. Wu, Z. Yu, and G. Liu Nemotron-research-tool-n1: exploring tool-using language models with reinforced reasoning. In The Fourteenth International Conference on Learning Representations, External Links: Link Cited by: §2.1.Source
  32. Zhao et al. (2025) W. Zhao, X. Wang, C. Ma, L. Kong, Z. Yang, M. Tuo, X. Shi, Y. Zhai, and X. Cai MUA-rl: multi-turn user-interacting agent reinforcement learning for agentic tool use. External Links: 2508.18669, Link Cited by: §2.1.Source
  33. Zheng et al. (2024) Y. Zheng, R. Zhang, J. Zhang, Y. YeYanhan, and Z. Luo LlamaFactory: unified efficient fine-tuning of 100+ language models. In Proceedings of the 62nd Annual Meeting of the Association for Computational Linguistics (Volume 3: System Demonstrations), pp. 400–410. Cited by: 1st item.
Figure 10
Figure 10: Diversity and statistical distributions of 191 synthesized environments.

  • •

    Figures 12 and 13 show the prompts for task filtering and environment-theme inference in the Task-driven Environment Discovery stage.

  • •

    Figures 14 and 15 present the prompts used to plan environment states, rules, and operations during the environment-modeling stage, while Figures 16 and 17 show the prompts for converting these blueprints into executable programs.

  • •

    Figures 18 and 19 show the system prompts for the testing agent and checking agent in the Dual-Agent Env Assessment stage, respectively.

Figures 20 and 21 show the prompts for generating the environment’s initial state and tasks, respectively. Figures 22 and 23 show the prompts for decomposing tasks into checklists and converting each check item into a verification function.

Figures 24 and 25 show the system prompts guiding LLMs complete tasks under the Non-conversation and Conversation settings. Figure 26 shows the system prompt for guiding a LLM to act as a user.

Figure 10 shows detailed statistics for the 191 synthesized environments. By embedding each environment’s theme description and applying t-SNE, we obtain Figure 10(a), where the point dispersion demonstrates the diversity of environment themes. Figures 10(b) and (c) show the distribution of tool counts across environments, while Figure 10(d) shows the distribution of state categories. Figure 10(e) shows synthesized scenario counts per environment after removing unqualified scenario.

In Example D, we provide a program example of synthesized environments, and Table 11 shows the corresponding tool interfaces.

  • •

    Initial State Configuration: Figure 27 shows an example of an initial state configuration, which is used to initialize the environment’s state data.

  • •

    Task: Figure 28 presents an example task under this initial state.

  • •

    State Check Functions: Figure 12 shows the checklist associated with the task, along with the check function for each checkpoint, which are used to compute a trajectory’s reward score.

  • •

    Non-Conversation: Table 13 shows a trajectory example of Qwen3-30B-A3B-Thinking-2507 with the environment under Non-Conv setting.

  • •

    Conversation: Table 14 shows a trajectory example of Qwen3-30B-A3B-Thinking-2507 under Conversation setting. Both trajectories correspond to the same task scenario.

  • •

    BFCL-v3 Multi-Turn (Patil et al., 2025): The BFCL-v3 Multi-Turn dataset evaluates the ability of LLMs to consistently and accurately perform function calls across multi-turn dialogues, covering 8 environments including vehicle control, transactions, ticketing, and file systems. In addition to the Base subset for standard tasks, it includes Missing Parameters, Missing Functions, and Long Context subsets to test handling of incomplete information, unavailable functions, and information-dense long contexts. The evaluation applies both state checking and response checking as dual criteria.

  • •

    Tau-Bench (Yao et al., 2025): Tau-Bench is a benchmark for evaluating LLM agents in realistic human–AI interaction scenarios, focusing on their ability to engage in multi-turn dialogues, invoke domain-specific APIs, and follow complex business rules. It includes two subsets: (1) Retail: Simulated customer service for retail, such as order cancellation/modification, returns and exchanges. (2) Airline: Simulated airline customer service, such as ticket booking, rescheduling, baggage and insurance handling, with more complex rules and greater reasoning challenges.

  • •

    ACEBench-Agent (Chen et al., 2025): It evaluates LLMs’ ability to perform multi-turn and multi-step tool calls in dynamic, real-world environments, covering scenarios such as mobile apps, food delivery platforms, financial services, and travel booking. It consists of two subsets: (1) Multi-turn: The user interacts with the LLM multiple times during the task, gradually providing information or adjusting requirements. (2) Multi-step: The user gives the task only once at the start, and the LLM autonomously calls tools until it decides the task is complete.

Table 9 present the statistics of each benchmark.

Table 9
Dataset# Env# Tools Per Env# Task
BFCL-v3 Multi-Turn816.13800
- Base816.13200
- Miss-Param816.13200
- Miss-Func816.13200
- Long-Context816.13200
Tau-Bench214175
- Retail115115
- Airline11350
ACEBench-Agent (EN)47.7550
- Multi-Step3620
- Multi-Turn47.7530
Table 9: Statistics of evaluation benchmarks.
View original tableOriginal Table 9

  • •

    SFT. We use Qwen3-30B-A3B-Thinking-2507 as the teacher model to interact with 140 environments under two interaction settings. We remove trajectories that contain invalid formats or are judged by the LLM as impossible to complete, resulting in about 9K final trajectories. For SFT, We use the LlamaFactory framework (Zheng et al., 2024). In thinking mode, Qwen3 automatically removes all rounds’ reasoning process when applying the chat_template. To let the model learn the reasoning in each round, we split an nn-round sample into nn sub-samples (corresponding rounds are 1,2,…,nn). Only the final round’s reasoning and action in each sub-sample are supervised and optimized (implemented via the mask_history hyperparameter). We train for 3 epochs with a learning rate of 1e-6, a maximum sequence length of 32K tokens, and an effective batch size of 256 after gradient accumulation.

  • •

    RL. We conduct RL using the Reinforce++ algorithm (Hu et al., 2025) within the ROLL framework (Wang et al., 2025). We retain the KL constraint with a coefficient of 0.1 and use a learning rate of 1.0×10−61.0\times 10^{-6}. In each training step, we sample 64 tasks and rollout 8 trajectories per task in the Non-Conversation setting, for a total of up to 200 training steps. The maximum trajectory length is set to 32K tokens, and the maximum generation length per step is capped at 4K tokens.

  • •

    Evaluation. We evaluate with a temperature of 0.7 and report the average over three runs. Following the Qwen3 setup, we keep only historical actions and remove historical reasoning process. For both baselines and our trained models, we use each model’s own function-calling interface to ensure consistent tool-use formats. Because ACEBench uses the [func_name(param)] prompt format by default, we modify the official code to support LLMs’ native function-calling interface. For Tau-Bench, we use GPT-4.1 in ReAct mode to simulate users. For the Multi-Turn subset of ACEBench-Agent, we likewise use GPT-4.1 for user simulation. We set the maximum context length to 64K to match the Long-Context subset of BFCL-v3.

Table 10
Model (Non-Thinking)BFCL Multi-TurnACEBench -AgentTau- Bench
Qwen3-4B6.8830.5620.15
+ APIGen\text{6.75}_{{\color[rgb]{0.7148,0.125,0.0859}\text{-0.13}}}\text{20.84}_{{\color[rgb]{0.7148,0.125,0.0859}\text{-9.72}}}\text{14.71}_{{\color[rgb]{0.7148,0.125,0.0859}\text{-5.44}}}
+ APIGen-MT\text{3.00}_{{\color[rgb]{0.7148,0.125,0.0859}\text{-3.88}}}\text{32.50}_{{\color[rgb]{0,0.2734,0}\text{+1.94}}}\text{33.93}_{{\color[rgb]{0,0.2734,0}\text{+13.78}}}
+ EnvScaler (SFT)\text{17.88}_{{\color[rgb]{0,0.2734,0}\text{+11.00}}}\text{41.67}_{{\color[rgb]{0,0.2734,0}\text{+11.11}}}\text{18.57}_{{\color[rgb]{0.7148,0.125,0.0859}\text{-1.58}}}
Table 10: Performance of Qwen3-4B after training on different datasets in non-thinking mode.
View original tableOriginal Table 10

Table 10 shows the performance of Qwen3-4B training on different SFT datasets in non-thinking mode. We also compare the APIGen (Liu et al., 2024) and APIGen-MT (Prabhakar et al., 2025) datasets because they only provide non-thinking samples. APIGen contains 60K single-turn tool-use examples; APIGen-MT contains 5K multi-turn examples, but they cover only two environments, which are identical to those in Tau-Bench. For EnvScaler, we remove the teacher LLM’s reasoning process and keep only the action sequences. First, APIGen leads to degraded performance on all benchmarks, indicating that relying solely on single-turn supervision cannot improve an LLM’s multi-turn tool-use capabilities. Due to the environments being the same as in Tau-Bench, APIGen-MT provides a significant boost on Tau-Bench, but it fails to transfer to BFCL-MT or ACEBench-Agent. Moreover, since this dataset uses only the Conversation setting, the model tends to talk to the user before using tools, which conflicts with the style of BFCL-MT. This highlights the importance of training in both settings. Finally, EnvScaler (SFT) brings significant improvements on BFCL-MT and ACEBench-Agent but slightly degrades performance on Tau-Bench. We believe this is because Tau-Bench relies more on reasoning, while in non-thinking mode the LLM learns only actions and not the reasoning chain, making it difficult to produce correct actions during evaluation.

Figure 11
Figure 11: Score and error distribution of environments in dual-agent loop assessment.

As stated in Section 3.4, we conducted 100 rounds of cyclic testing for each synthetic environment and retained only those with a test success rate above 0.85. From 266 synthetic environments, we selected 191, corresponding to a rejection rate of 28.2%. In Figure 11 (a), we present the distribution of scores for all environments in the dual-agent loop assessment. To systematically analyze the reasons for environment failures, we extract all test cases identified as failures from the logs in the dual-agent loop quality inspection. We use Claude-4.5-Sonnet to classify these errors. As shown in Figure 11 (b), the primary issue is type errors or attribute errors, followed by the consistency of state management.

In this paper, we only use ChatGPT5^{5} for grammar proofreading and spell checking.

Figure 12
Figure 12: The prompt for filtering tasks situated within a domain-specific, stateful environment.
Figure 13
Figure 13: The prompt for inferring environment description from existing tasks.
Figure 14
Figure 14: The prompt for planning state and rules of the environment.
Figure 15
Figure 15: The prompt for planning tool operations of the environment.
Figure 16
Figure 16: The prompt for programmatically converting states into the class definition.
Figure 17
Figure 17: The prompt for programmatically converting operation to class-method.
Figure 18
Figure 18: The prompt for initializing the testing agent during environments assessment.
Figure 19
Figure 19: The prompt for initializing the checking agent during environments assessment.
Figure 20
Figure 20: The prompt for generating environment’s initial state data.
Figure 21
Figure 21: The prompt for generating a task under the specific environment and state.
Figure 22
Figure 22: The prompt for generating verification checklist for a task.
Figure 23
Figure 23: The prompt for programmatically converting each checkpoint to a verification function.
Figure 24
Figure 24: The system prompt for prompting LLM agents under the Non-conversation setting.
Figure 25
Figure 25: The system prompt for prompting LLM agents under the Conversation setting.
Figure 26
Figure 26: The system prompt for prompting the LLM to act as a user under the Conversation setting.

Example 1: Program Example of Synthesized Environment

⬇ from typing import Dict, List, TypedDict import uuid import time class UserInfo(TypedDict): _id: str phone_number: str name: str profile_info: str class ContactInfo(TypedDict): _id: str contact_id: str contact_phone_number: str contact_name: str class MessageInfo(TypedDict): message_id: str sender_id: str receiver_id: str receiver_phone_number: str content: str timestamp: str delivery_status: str read_status: str class ConversationInfo(TypedDict): conversation_id: str user_ids: List[str] message_id: str class MobileMessagingApplication: def __init__(self, config: dict): self.users: Dict[str, UserInfo] = {} self.contacts: Dict[str, ContactInfo] = {} self.messages: Dict[str, MessageInfo] = {} self.conversations: Dict[str, ConversationInfo] = {} # Constraints: # - Only registered users can send messages # - Phone numbers must be valid before sending messages # - Message delivery status must be updated (e.g., sent, delivered, failed) # - Conversation history is retained for each user-contact pair # - Duplicate messages to the same phone number are allowed but tracked separately self.config = config def get_user_by_phone_number(self, phone_number: str) -> dict: """Look up a user by their phone number. Constraints: - Only registered users are present in the system. - Phone number must match a user entry; otherwise, return error.""" for user in self.users.values(): if user["phone_number"] == phone_number: return {"success": True, "data": user} return {"success": False, "error": "User with this phone number not found"} def get_user_by_id(self, user_id: str) -> dict: """Retrieve user information by user ID. Constraints: - user_id must correspond to an existing registered user.""" user = self.users.get(user_id) if user is None: return { "success": False, "error": "User not found" } return { "success": True, "data": user } def list_all_users(self) -> dict: """List all registered user accounts. Constraints: - Returns the current set of registered users. - No failure unless system corruption (not handled here).""" user_list = list(self.users.values()) return { "success": True, "data": user_list } def get_contact_by_phone_number(self, phone_number: str) -> dict: """Retrieve contact information using a contact’s phone number. Constraints: - The provided phone number must correspond to an existing contact.""" for contact in self.contacts.values(): if contact["contact_phone_number"] == phone_number: return {"success": True, "data": contact} return {"success": False, "error": "No contact found with this phone number."} def get_contact_by_id(self, contact_id: str) -> dict: """Look up and return contact info by contact ID. Constraints: - Contact must exist in the system.""" contact = self.contacts.get(contact_id) if contact is None: return { "success": False, "error": "Contact not found" } return { "success": True, "data": contact } def list_user_contacts(self, user_id: str) -> dict: """Lists all contacts associated with a particular user. Constraints: - The provided user_id must correspond to a registered user.""" if user_id not in self.users: return {"success": False, "error": "User does not exist"} contacts = [ contact_info for contact_info in self.contacts.values() if contact_info.get("contact_id") == user_id ] return {"success": True, "data": contacts} def validate_phone_number(self, phone_number: str) -> dict: """Check if a phone number is valid for messaging. Constraints: - Phone number should be a non-empty string. - Phone number should only contain digits, possibly beginning with ’+’. - Length must be between 10 and 15 (excluding ’+’).""" if not isinstance(phone_number, str): return { "success": False, "error": "Phone number must be a string" } stripped = phone_number.lstrip(’+’) if not stripped.isdigit(): return { "success": True, "data": {"valid": False, "reason": "Phone number must contain only digits after optional ’+’"} } if len(stripped) < 10 or len(stripped) > 15: return { "success": True, "data": {"valid": False, "reason": "Phone number length must be between 10 and 15 digits"} } # Passed all checks return { "success": True, "data": {"valid": True, "reason": "Phone number is valid for messaging"} } def get_messages_by_phone_number(self, phone_number: str) -> dict: """Retrieve all messages (sent or received) associated with the specified phone number. Constraints: - Matches both messages where receiver_phone_number == phone_number, and messages sent by users whose phone_number == phone_number. - Returns empty list if no matches found; this is not considered an error. - Input phone number should be a string.""" if not isinstance(phone_number, str): return { "success": False, "error": "Invalid phone number format" } # Find all user IDs whose phone number matches the input (for sent messages) sender_ids = [ user_id for user_id, user_info in self.users.items() if user_info["phone_number"] == phone_number ] result = [ msg for msg in self.messages.values() if ( msg["receiver_phone_number"] == phone_number or msg["sender_id"] in sender_ids)] return { "success": True, "data": result } def get_conversation_by_user_and_contact(self, user_id: str, contact_id: str) -> dict: """Get the conversation history (messages) between a user and a given contact. Constraints: - User id must exist in the platform. - Contact id must exist in contacts. - Returns all messages in the user-contact conversation(s). """ if user_id not in self.users: return { "success": False, "error": "User not found" } if contact_id not in self.contacts: return { "success": False, "error": "Contact not found" } # Find relevant conversations: conversations = [ convo for convo in self.conversations.values() if user_id in convo["user_ids"] and contact_id in convo["user_ids"] ] if not conversations: return { "success": False, "error": "No conversation found between user and contact" } # Gather all related message infos: messages = [] for convo in conversations: msg_id = convo["message_id"] if msg_id in self.messages: messages.append(self.messages[msg_id]) return { "success": True, "data": messages } def get_message_by_id(self, message_id: str) -> dict: """Retrieve information for a specific message given its unique message ID. Constraints: - The message ID must exist in the environment’s messages.""" message = self.messages.get(message_id) if not message: return { "success": False, "error": "Message not found" } return { "success": True, "data": message } def get_message_delivery_status(self, message_id: str) -> dict: """Check and return the current delivery status of a specific message. Constraints: - message_id must exist in the system.""" msg = self.messages.get(message_id) if not msg: return { "success": False, "error": "Message ID not found" } return { "success": True, "data": {"message_id": message_id, "delivery_status": msg["delivery_status"]}} def send_message(self, sender_id: str, receiver_phone_number: str, content: str) -> dict: """Create and send a new message from a registered user to a valid phone number. Constraints: - Only registered users can send messages. - Phone numbers must be valid before sending. - Message delivery status must be set (’sent’). - Conversation history must include this message. - Content must not be empty.""" # Sender must be a registered user if sender_id not in self.users: return {"success": False, "error": "Sender is not a registered user."} # Content must not be empty if not content or content.strip() == "": return {"success": False, "error": "Message content cannot be empty."} # Validate phone number if not hasattr(self, "validate_phone_number") or not callable(self.validate_phone_number): return {"success": False, "error": "Phone number validation method is unavailable."} valid_check = self.validate_phone_number(receiver_phone_number) if not valid_check.get("success", False): return {"success": False, "error": "Receiver phone number is invalid."} # Determine receiver user (if exists) receiver_id = None for uid, user in self.users.items(): if user["phone_number"] == receiver_phone_number: receiver_id = uid break # Create unique message_id (use combination of sender, receiver, timestamp, and an increment) message_id = str(uuid.uuid4()) timestamp = str(time.time()) # Initial delivery status and read status delivery_status = "sent" read_status = "unread" # Create message entry message_info = { "message_id": message_id, "sender_id": sender_id, "receiver_id": receiver_id if receiver_id else "", "receiver_phone_number": receiver_phone_number, "content": content, "timestamp": timestamp, "delivery_status": delivery_status, "read_status": read_status } self.messages[message_id] = message_info # Maintain conversation history: # Find if a conversation exists between sender and receiver (user_id or phone_number) # Use sorted user_ids (if possible), else pseudo user_id from phone number user_ids = [sender_id] if receiver_id: user_ids.append(receiver_id) else: # Create pseudo id for phone number to track conversation user_ids.append(f"phone:{receiver_phone_number}") user_ids_sorted = sorted(user_ids) # Try to find existing conversation conversation_id = None for cid, conv in self.conversations.items(): if sorted(conv[’user_ids’]) == user_ids_sorted: conversation_id = cid break if conversation_id is None: # Create a new conversation conversation_id = str(uuid.uuid4()) self.conversations[conversation_id] = { "conversation_id": conversation_id, "user_ids": user_ids_sorted, "message_id": message_id } else: # Update conversation: for simplicity, replace message_id (could be made a list in real impl.) # To keep track, we simply overwrite the latest message_id (history is in messages) self.conversations[conversation_id]["message_id"] = message_id return {"success": True, "message": "Message sent successfully."} def update_message_delivery_status(self, message_id: str, new_status: str) -> dict: """Set or update the delivery status for a particular message. Constraints: - The message_id must exist. - Optionally, enforce that new_status is among allowed values (’sent’, ’delivered’, ’failed’).""" allowed_statuses = {"sent", "delivered", "failed"} if message_id not in self.messages: return { "success": False, "error": "Message not found" } if new_status not in allowed_statuses: return { "success": False, "error": f"Invalid delivery status: {new_status}" } self.messages[message_id]["delivery_status"] = new_status return { "success": True, "message": f"Delivery status updated for message {message_id}" } def create_contact(self, contact_id: str, contact_phone_number: str, contact_name: str) -> dict: """Add a new contact to the specified user’s contact list. Constraints: - Only registered users can add contacts. - Phone numbers must be valid before creating a contact. - Duplicate contacts (same phone number) are allowed but tracked separately.""" # Check that the user exists if contact_id not in self.users: return { "success": False, "error": "User does not exist" } # Validate phone number using the environment’s function if not hasattr(self, "validate_phone_number"): return { "success": False, "error": "Phone number validation not available" } validation_result = self.validate_phone_number(contact_phone_number) if not (isinstance(validation_result, dict) and validation_result.get("success")): return { "success": False, "error": "Phone number is invalid" } # Generate new contact _id (ensure uniqueness) new_contact_id = f"contact_{len(self.contacts)+1}" # Create and store the contact contact_info = { "_id": new_contact_id, "contact_id": contact_id, "contact_phone_number": contact_phone_number, "contact_name": contact_name } self.contacts[new_contact_id] = contact_info return { "success": True, "message": "Contact created" } def add_message_to_conversation(self, conversation_id: str, message_id: str) -> dict: """Insert a new message into the conversation history for the appropriate user-contact pair. Constraints: - Both the conversation and the message must exist. - Each message can be tracked in the history; duplicate messages to same phone are allowed but tracked separately. - User IDs in message context should match conversation participants (not strictly enforced).""" # Check conversation existence if conversation_id not in self.conversations: return { "success": False, "error": "Conversation does not exist." } if message_id not in self.messages: return { "success": False, "error": "Message does not exist." } # Get conversation and message info conv_info = self.conversations[conversation_id] msg_info = self.messages[message_id] # Optionally check participants (message sender/receiver in conversation user_ids) relevant_user_ids = conv_info.get("user_ids", []) msg_participants = [msg_info["sender_id"], msg_info["receiver_id"]] missing = [uid for uid in msg_participants if uid not in relevant_user_ids] # Not strictly enforced per constraints, but warn if mismatch. # For this environment, conversation history is retained for each user/contact pair. # Since ConversationInfo maps one message_id, we append a new entry with same conv_id and user_ids new_convo_entry = { "conversation_id": conversation_id, "user_ids": relevant_user_ids, "message_id": message_id } # Insert new entry into conversations dict with a unique key # Use ’conversation_id:message_id’ as a compound key to allow duplicate messages convo_key = f"{conversation_id}:{message_id}" self.conversations[convo_key] = new_convo_entry return { "success": True, "message": "Message added to conversation history." } def create_conversation(self, user_id1: str, user_id2: str) -> dict: """Initiate a new conversation record between two users or a user and a contact, if such a conversation does not already exist. Constraints: - Both participants must exist (in users or contacts). - Conversation between same pair must be unique. - Participants must not be identical.""" # Check that both participants exist in the system valid_ids = set(self.users.keys()) | set(self.contacts.keys()) if user_id1 not in valid_ids: return { "success": False, "error": f"Participant 1 ({user_id1}) does not exist." } if user_id2 not in valid_ids: return { "success": False, "error": f"Participant 2 ({user_id2}) does not exist." } if user_id1 == user_id2: return { "success": False, "error": "Cannot create conversation with oneself." } # Check if a conversation between these two IDs already exists for conv in self.conversations.values(): ids_set = set(conv["user_ids"]) if ids_set == set([user_id1, user_id2]): return { "success": False, "error": "Conversation between these participants already exists.", "conversation_id": conv["conversation_id"] } conversation_id = str(uuid.uuid4()) conversation_info: ConversationInfo = { "conversation_id": conversation_id, "user_ids": [user_id1, user_id2], "message_id": "" # Initial, no message yet } self.conversations[conversation_id] = conversation_info return { "success": True, "message": "Conversation created", "conversation": conversation_info } def mark_message_as_read(self, message_id: str) -> dict: """Mark a message as read by updating its read_status field. Constraints: - message_id must exist in the system. - Operation is idempotent; if already ’read’, report success.""" message = self.messages.get(message_id) if not message: return { "success": False, "error": "Message not found." } if message["read_status"] == "read": # Already marked as read, treat as success return { "success": True, "message": "Message marked as read." } # Mark as read message["read_status"] = "read" return { "success": True, "message": "Message marked as read." } def delete_message(self, message_id: str) -> dict: """Remove a message from the system. If message exists, it’s deleted and any references in conversations are also removed. Constraints: - Message must exist in the system. - References to this message in conversation objects are also removed for integrity.""" if message_id not in self.messages: return {"success": False, "error": f"Message {message_id} does not exist."} # Remove references in conversations for conv in self.conversations.values(): if conv["message_id"] == message_id: conv["message_id"] = "" # Remove reference (could also delete the conversation if message is sole content) # Alternatively, could maintain a messages list if the schema changes # Remove the message itself del self.messages[message_id] return { "success": True, "message": f"Message {message_id} has been deleted." } def archive_conversation(self, user_id: str, conversation_id: str) -> dict: """Archive or hide a conversation for a specific user. Constraints: - Only registered users may archive conversations. - Conversation must exist. - User must be a participant in the conversation. - Archiving is idempotent (archiving again is still success).""" # Ensure tracking exists if not hasattr(self, "archived_conversations"): self.archived_conversations: Dict[str, List[str]] = {} if user_id not in self.users: return { "success": False, "error": "User does not exist." } if conversation_id not in self.conversations: return { "success": False, "error": "Conversation does not exist." } conversation_info = self.conversations[conversation_id] if user_id not in conversation_info["user_ids"]: return { "success": False, "error": "User is not a participant in the conversation." } archived_list = self.archived_conversations.setdefault(user_id, []) if conversation_id not in archived_list: archived_list.append(conversation_id) return { "success": True, "message": "Conversation archived for user." } def delete_conversation(self, conversation_id: str, requestor_id: str) -> dict: """Remove a conversation from history if the action is allowed. Constraints: - Only admins or users who are part of the conversation can delete it. - If conversation does not exist, fail. - Conversation removal does not delete referenced messages.""" # Check if conversation exists conv = self.conversations.get(conversation_id) if not conv: return { "success": False, "error": "Conversation does not exist." } # Check requestor existence user_info = self.users.get(requestor_id) if not user_info: return { "success": False, "error": "Requestor does not exist." } # Permission check: Admin or participant is_admin = (user_info.get("profile_info", "") == "admin") is_participant = requestor_id in conv.get("user_ids", []) if not (is_admin or is_participant): return { "success": False, "error": "Permission denied." } # Delete the conversation del self.conversations[conversation_id] return { "success": True, "message": "Conversation deleted successfully." }

Table 11
Tool NameDescriptionParameters
get_user_by_phone_ numberLook up a user by their phone number.
Args: phone_number (str): The phone number to search for.
phone_number
get_user_by_idRetrieve user information by user ID.
Args: user_id (str): The unique ID of the user to be retrieved.
user_id
list_all_usersList all registered user accounts.
Args: None
None
get_contact_by_phone_ numberRetrieve contact information using a contact’s phone number.
Args: phone_number (str): The phone number to look up in contacts.
phone_number
get_contact_by_idLook up and return contact info by contact ID.
Args: contact_id (str): Unique identifier of the contact.
contact_id
list_user_contactsLists all contacts associated with a particular user.
Args: user_id (str): The unique identifier for the user whose contacts are to be listed.
user_id
validate_phone_ numberCheck if a phone number is valid for messaging.
Args: phone_number (str): The phone number to validate.
phone_number
get_messages_by_ phone_numberRetrieve all messages (sent or received) associated with the specified phone number.
Args: phone_number (str): The phone number for which to fetch messages.
phone_number
get_conversation_by_ user_and_contactGet the conversation history (messages) between a user and a given contact.
Args: user_id (str): The user’s id. contact_id (str): The contact’s id.
user_id, contact_id
get_message_by_idRetrieve information for a specific message given its unique message ID.
Args: message_id (str): The unique identifier of the message.
message_id
get_message_delivery_ statusCheck and return the current delivery status of a specific message.
Args: message_id (str): The unique identifier of the message to query.
message_id
send_messageCreate and send a new message from a registered user to a valid phone number.
Args: sender_id (str): Registered user’s ID of the message sender. receiver_phone_number (str): The recipient’s phone number. content (str): The textual content of the message.
sender_id, receiver_phone_number, content
update_message_ delivery_statusSet or update the delivery status for a particular message.
Args: message_id (str): The unique identifier of the message to update. new_status (str): The new delivery status (e.g., ’sent’, ’delivered’, ’failed’).
message_id, new_status
create_contactAdd a new contact to the specified user’s contact list.
Args: contact_id (str): The user id for whom the contact is being added. contact_phone_number (str): The contact’s phone number. contact_name (str): The contact’s name.
contact_id, contact_phone_number, contact_name
add_message_to_ conversationInsert a new message into the conversation history for the appropriate user-contact pair.
Args: conversation_id (str): ID of the conversation history to add the message to. message_id (str): ID of the message to insert (must exist and be valid).
conversation_id, message_id
create_conversationInitiate a new conversation record between two users or a user and a contact, if such a conversation does not already exist.
Args: user_id1 (str): The ID of the first user/participant. user_id2 (str): The ID of the second user/participant.
user_id1, user_id2
mark_message_as_readMark a message as read by updating its read_status field.
Args: message_id (str): The unique identifier of the message to update.
message_id
delete_messageRemove a message from the system. If message exists, it’s deleted and any references in conversations are also removed.
Args: message_id (str): Unique identifier of the message to remove.
message_id
archive_conversationArchive or hide a conversation for a specific user.
Args: user_id (str): The user who wants to archive the conversation. conversation_id (str): The conversation to archive.
user_id, conversation_id
delete_conversationRemove a conversation from history if the action is allowed.
Args: conversation_id (str): The unique ID of the conversation to remove. requestor_id (str): The user requesting the deletion.
conversation_id, requestor_id
Table 11: An example of tool interfaces provided by synthesized environments to the LLM agent.
View original tableOriginal Table 11
Figure 27
Figure 27: An example of initial state data configuration for the environment.
Figure 28
Figure 28: An example task under the above state configuration.
Table 12
No.CheckpointCheck Function
1Has Gabby Fields been added as a contact for user USR2 (Brandon Lee)?⬇ def check_func(final_state): for contact in final_state.get("contacts", {}).values(): if (contact.get("contact_id") == "USR2" and contact.get("contact_phone_number") == "+17165558888" and contact.get("contact_name") == "Gabby Fields"): return True return False
2Has the mobile number of Gabby Fields been set to "+17165558888"?⬇ def check_func(final_state): for contact in final_state.get("contacts", {}).values(): if contact.get("contact_name") == "Gabby Fields" and contact.get("contact_phone_number") == "+17165558888": return True return False
3Has the new corrected message from USR2 to Gabby Fields been created with text "Hi Gabby, the meeting is now at 4:30pm ET. Please confirm."?⬇ def check_func(final_state): target_text = "Hi Gabby, the meeting is now at 4:30pm ET. Please confirm." for msg in final_state.get("messages", {}).values(): if (msg.get("sender_id") == "USR2" and msg.get("receiver_id") == "" and msg.get("receiver_phone_number") == "+17165558888" and msg.get("content") == target_text): return True return False
4Has the delivery_status of the new corrected message been set to "delivered"?⬇ def check_func(final_state): target_content = "Hi Gabby, the meeting is now at 4:30pm ET. Please confirm." for msg in final_state.get("messages", {}).values(): if (msg.get("sender_id") == "USR2" and msg.get("receiver_phone_number") == "+17165558888" and msg.get("content") == target_content and msg.get("delivery_status") == "delivered"): return True return False
5Has the new corrected message been linked to the existing Brandon–Gabby conversation?⬇ def check_func(final_state): conv = final_state.get("conversations", {}).get("CONV003") if not conv: return False return conv.get("message_id") != "MSG005"
6Has the Brandon–Gabby conversation been archived?⬇ def check_func(final_state): target_users = {"USR2", "phone:+17165558888"} for conv in final_state.get("conversations", {}).values(): if set(conv.get("user_ids", [])) == target_users: return conv.get("archived") is True return False
7Has the old failed message previously sent by USR2 to Gabby Fields been deleted?⬇ def check_func(final_state): return "MSG005" not in final_state.get("messages", {})
8Has the status-update message from USR2 to Alice Chan (USR1) been created with text "Update: re-sent Gabby’s invite and delivery confirmed."?⬇ def check_func(final_state): target_text = "Update: re-sent Gabby’s invite and delivery confirmed." for msg in final_state.get("messages", {}).values(): if msg.get("sender_id") == "USR2" and msg.get("receiver_id") == "USR1" and msg.get("content") == target_text: return True return False
9Has Alice Chan’s previously unread message from USR2 been marked as "read"?⬇ def check_func(final_state): msg = final_state["messages"].get("MSG002") return msg.get("read_status") == "read" if msg else False
Table 12: The checklist and state-check functions corresponding to the task.
View original tableOriginal Table 12
Table 13
TurnInteraction
1Observation: [Task] Recover the failed outreach to Gabby Fields and report status: 1) As Brandon Lee (USR2), add Gabby Fields as a contact using her mobile number +17165558888. Validate the number before sending. 2) Re-send a corrected invite to Gabby from Brandon: “Hi Gabby, the meeting is now at 4:30pm ET. Please confirm.” Update the new message’s delivery status to “delivered.” 3) Link the new message to the existing Brandon–Gabby conversation and then archive that conversation. After successfully sending the new message, delete the old failed message Brandon previously sent to Gabby. 4) Send a short status update from Brandon to Alice Chan (USR1): “Update: re-sent Gabby’s invite and delivery confirmed.” Then mark Alice’s previously unread message from Brandon as “read.”
Action: Function(name="validate_phone_number", arguments={"phone_number": "+17165558888"})
2Observation: [Tool Result] {’success’: True, ’data’: {’valid’: True, ’reason’: ’Phone number is valid for messaging’}}
Action: Function(name="create_contact", arguments={"contact_id": "USR2", "contact_phone_number": "+17165558888", "contact_name": "Gabby Fields"})
3Observation: [Tool Result] {’success’: True, ’message’: ’Contact created’}
Action: Function(name="get_contact_by_phone_number", arguments={"phone_number": "+17165558888"})
4Observation: [Tool Result] {’success’: True, ’data’: {’_id’: ’contact_2’, ’contact_id’: ’USR1’, ’contact_phone_number’: ’+17165558888’, ’contact_name’: ’Gabby Fields’}}
Action: Function(name="get_conversation_by_user_and_contact", arguments={"user_id": "USR2", "contact_id": "contact_2"})
5Observation: [Tool Result] {’success’: False, ’error’: ’No conversation found between user and contact’}
Action: Function(name="get_user_by_phone_number", arguments={"phone_number": "+17165558888"})
6Observation: [Tool Result] {’success’: False, ’error’: ’User with this phone number not found’}
Action: Function(name="create_conversation", arguments={"user_id1": "USR2", "user_id2": "contact_2"})
7Observation: [Tool Result] {’success’: True, ’message’: ’Conversation created’, ’conversation’: {’conversation_id’: ’50aecb26-277a-478f-b9fe-cf5d89330781’, ’user_ids’: [’USR2’, ’contact_2’], ’message_id’: ’’}}
Action: Function(name="send_message", arguments={"sender_id": "USR2", "receiver_phone_number": "+17165558888", "content": "Hi Gabby, the meeting is now at 4:30pm ET. Please confirm."})
8Observation: [Tool Result] {’success’: True, ’message’: ’Message sent successfully.’}
Action: Function(name="get_messages_by_phone_number", arguments={"phone_number": "+17165558888"})
9Observation: [Tool Result] {’success’: True, ’data’: [{’message_id’: ’MSG005’, ’sender_id’: ’USR2’, ’receiver_id’: ’’, ’receiver_phone_number’: ’+17165558888’, ’content’: ’Gabby, please join the video call at 4pm.’, ’timestamp’: ’2024-04-02T08:45:19Z’, ’delivery_status’: ’failed’, ’read_status’: ’unread’}, {’message_id’: ’5b5f4580-f7db-4550-b6bd-0d7ef221741f’, ’sender_id’: ’USR2’, ’receiver_id’: ’’, ’receiver_phone_number’: ’+17165558888’, ’content’: ’Hi Gabby, the meeting is now at 4:30pm ET. Please confirm.’, ’timestamp’: ’1763026165.0235567’, ’delivery_status’: ’sent’, ’read_status’: ’unread’}]}
Action: Function(name="update_message_delivery_status", arguments={"message_id": "5b5f4580-f7db-4550-b6bd-0d7ef221741f", "new_status": "delivered"})
10Observation: [Tool Result] {’success’: True, ’message’: ’Delivery status updated for message 5b5f4580-f7db-4550-b6bd-0d7ef221741f’}
Action: Function(name="add_message_to_conversation", arguments={"conversation_id": "50aecb26-277a-478f-b9fe-cf5d89330781", "message_id": "5b5f4580-f7db-4550-b6bd-0d7ef221741f"})
11Observation: [Tool Result] {’success’: True, ’message’: ’Message added to conversation history.’}
Action: Function(name="archive_conversation", arguments={"user_id": "USR2", "conversation_id": "50aecb26-277a-478f-b9fe-cf5d89330781"})
12Observation: [Tool Result] {’success’: True, ’message’: ’Conversation archived for user.’}
Action: Function(name="delete_message", arguments={"message_id": "MSG005"})
13Observation: [Tool Result] {’success’: True, ’message’: ’Message MSG005 has been deleted.’}
Action: Function(name="get_user_by_id", arguments={"user_id": "USR1"})
14Observation: [Tool Result] {’success’: True, ’data’: {’_id’: ’USR1’, ’phone_number’: ’+15551234567’, ’name’: ’Alice Chan’, ’profile_info’: ’admin’}}
Action: Function(name="send_message", arguments={"sender_id": "USR2", "receiver_phone_number": "+15551234567", "content": "Update: re-sent Gabby’s invite and delivery confirmed."})
15Observation: [Tool Result] {’success’: True, ’message’: ’Message sent successfully.’}
Action: Function(name="get_messages_by_phone_number", arguments={"phone_number": "+15551234567"})
16Observation: [Tool Result] {’success’: True, ’data’: [{’message_id’: ’MSG001’, ’sender_id’: ’USR1’, ’receiver_id’: ’USR2’, ’receiver_phone_number’: ’+15559874566’, ’content’: ’Hey Brandon, can you send the report by Friday?’, ’timestamp’: ’2024-02-17T14:38:05Z’, ’delivery_status’: ’delivered’, ’read_status’: ’read’}, {’message_id’: ’MSG002’, ’sender_id’: ’USR2’, ’receiver_id’: ’USR1’, ’receiver_phone_number’: ’+15551234567’, ’content’: "Hi Alice, yes I’ll have it done tomorrow.", ’timestamp’: ’2024-02-17T15:00:51Z’, ’delivery_status’: ’delivered’, ’read_status’: ’unread’}, {’message_id’: ’MSG006’, ’sender_id’: ’USR5’, ’receiver_id’: ’’, ’receiver_phone_number’: ’+15551234567’, ’content’: ’Alice, your settings have been updated securely.’, ’timestamp’: ’2024-06-01T10:02:46Z’, ’delivery_status’: ’sent’, ’read_status’: ’unread’}, {’message_id’: ’MSG007’, ’sender_id’: ’USR1’, ’receiver_id’: ’USR5’, ’receiver_phone_number’: ’+812345678901’, ’content’: ’Emi, the new version is available for testing.’, ’timestamp’: ’2024-05-15T07:17:22Z’, ’delivery_status’: ’delivered’, ’read_status’: ’read’}, {’message_id’: ’08e0a7b1-5039-433d-9dfd-7f7e4359d548’, ’sender_id’: ’USR2’, ’receiver_id’: ’USR1’, ’receiver_phone_number’: ’+15551234567’, ’content’: ’Update: re-sent Gabby’s invite and delivery confirmed.’, ’timestamp’: ’1763026220.1726387’, ’delivery_status’: ’sent’, ’read_status’: ’unread’}]}
Action: Function(name="mark_message_as_read", arguments={"message_id": "MSG002"})
17Observation: [Tool Result] {’success’: True, ’message’: ’Message marked as read.’}
Action: Task Completed
Table 13: A trajectory example of Non-Conversation setting (The reasoning process <think>…</think> is ignored).
View original tableOriginal Table 13
Table 14
TurnInteraction
1Observation: [User] Hey! I need some help with an outreach issue involving Gabby Fields. Can you assist me with getting her contact added?
Action: To add Gabby Fields as a contact, I’ll need her phone number. Could you please provide Gabby’s phone number so I can validate it and add her to your contact list?
2Observation: [User] Sure, Gabby’s mobile number is +17165558888. Let me know once you’ve validated it and added her to my contacts!
Action: Function(name="validate_phone_number", arguments={"phone_number": "+17165558888"})
3Observation: [Tool Result] {’success’: True, ’data’: {’valid’: True, ’reason’: ’Phone number is valid for messaging’}}
Action: The phone number +17165558888 has been validated as valid for messaging. To proceed with adding Gabby Fields to your contacts, I’ll need your user ID (the unique identifier associated with your account). Could you please share your user ID so I can complete this request?
4Observation: [User] You can use my user ID: Brandon Lee (USR2) for this. Please go ahead and add Gabby Fields using that info.
Action: Function(name="create_contact", arguments={"contact_id": "USR2", "contact_phone_number": "+17165558888", "contact_name": "Gabby Fields"})
5Observation: [Tool Result] {’success’: True, ’message’: ’Contact created’}
Action: Gabby Fields (phone: +17165558888) has been successfully added to your contacts as Gabby Fields. Your contact list now includes this entry under your account (USR2). Let me know if you’d like to send a message to Gabby or need further assistance!
6Observation: [User] Great, thanks! Next, I want to send Gabby a new invite from Brandon. The message should be: “Hi Gabby, the meeting is now at 4:30pm ET. Please confirm.” Please send it and update the delivery status to “delivered.”
Action: Function(name="send_message", arguments={"sender_id": "USR2", "receiver_phone_number": "+17165558888", "content": "Hi Gabby, the meeting is now at 4:30pm ET. Please confirm."})
7Observation: [Tool Result] {’success’: True, ’message’: ’Message sent successfully.’}
Action: Function(name="update_message_delivery_status", arguments={"message_id": "MSG123", "new_status": "delivered"})
8Observation: [Tool Result] {’success’: False, ’error’: ’Message not found’}
Action: Function(name="get_messages_by_phone_number", arguments={"phone_number": "+17165558888"})
9Observation: [Tool Result] {’success’: True, ’data’: [{’message_id’: ’MSG005’, ’sender_id’: ’USR2’, ’receiver_id’: ’’, ’receiver_phone_number’: ’+17165558888’, ’content’: ’Gabby, please join the video call at 4pm.’, ’timestamp’: ’2024-04-02T08:45:19Z’, ’delivery_status’: ’failed’, ’read_status’: ’unread’}, {’message_id’: ’c02f3c58-a015-40ee-b034-390b649c472e’, ’sender_id’: ’USR2’, ’receiver_id’: ’’, ’receiver_phone_number’: ’+17165558888’, ’content’: ’Hi Gabby, the meeting is now at 4:30pm ET. Please confirm.’, ’timestamp’: ’1763054273.124194’, ’delivery_status’: ’sent’, ’read_status’: ’unread’}]}
Action: Function(name="update_message_delivery_status", arguments={"message_id": "c02f3c58-a015-40ee-b034-390b649c472e", "new_status": "delivered"})
10Observation: [Tool Result] {’success’: True, ’message’: ’Delivery status updated for message c02f3c58-a015-40ee-b034-390b649c472e’}
Action: The message to Gabby Fields has been successfully sent and the delivery status updated to "delivered". Here’s the confirmation: Message Content: "Hi Gabby, the meeting is now at 4:30pm ET. Please confirm." Delivery Status: Delivered Message ID: c02f3c58-a015-40ee-b034-390b649c472e Let me know if you’d like to send another message or need further assistance!
11Observation: [User] Perfect! Can you please link this new message to the existing Brandon–Gabby conversation? Once that’s done, go ahead and archive the whole conversation for me.
Action: Function(name="create_conversation", arguments={"user_id1": "USR2", "user_id2": "+17165558888"})
12Observation: [Tool Result] {’success’: False, ’error’: ’Participant 2 (+17165558888) does not exist.’}
Action: Function(name="get_contact_by_phone_number", arguments={"phone_number": "+17165558888"})
13Observation: [Tool Result] {’success’: True, ’data’: {’_id’: ’contact_2’, ’contact_id’: ’USR1’, ’contact_phone_number’: ’+17165558888’, ’contact_name’: ’Gabby Fields’}}
Action: Function(name="create_conversation", arguments={"user_id1": "USR2", "user_id2": "contact_2"})
14Observation: [Tool Result] {’success’: True, ’message’: ’Conversation created’, ’conversation’: {’conversation_id’: ’c3d610bf-1100-405f-8897-8df5cea96b1d’, ’user_ids’: [’USR2’, ’contact_2’], ’message_id’: ’’}}
Action: Function(name="add_message_to_conversation", arguments={"conversation_id": "c3d610bf-1100-405f-8897-8df5cea96b1d", "message_id": "c02f3c58-a015-40ee-b034-390b649c472e"})
15Observation: [Tool Result] {’success’: True, ’message’: ’Message added to conversation history.’}
Action: Function(name="archive_conversation", arguments={"user_id": "USR2", "conversation_id": "c3d610bf-1100-405f-8897-8df5cea96b1d"})
16Observation: [Tool Result] {’success’: True, ’message’: ’Conversation archived for user.’}
Action: The message has been successfully linked to the Brandon–Gabby conversation (ID: ‘c3d610bf-1100-405f-8897-8df5cea96b1d‘), and the entire conversation has been archived for your account (USR2). Summary: Message "Hi Gabby, the meeting is now at 4:30pm ET. Please confirm." linked to conversation Conversation archived for future reference Let me know if you’d like to unarchive or take any other action!
17Observation: [User] Thanks! Now, please delete the old failed message Brandon sent to Gabby from that conversation. Let me know once it’s removed.
Action: Function(name="delete_message", arguments={"message_id": "MSG005"})
18Observation: [Tool Result] {’success’: True, ’message’: ’Message MSG005 has been deleted.’}
Action: The old failed message (ID: ‘MSG005‘) has been successfully deleted from the conversation. The conversation now only includes the confirmed message: "Hi Gabby, the meeting is now at 4:30pm ET. Please confirm." Let me know if you need further cleanup or assistance!
19Observation: [User] Great, thank you. Lastly, send this short status update from Brandon to Alice Chan: “Update: re-sent Gabby’s invite and delivery confirmed.” And please mark Alice’s previously unread message from Brandon as “read.” Let me know when that’s done.
Action: To send the status update to Alice Chan and mark her unread message as read, I’ll need Alice Chan’s phone number to proceed. Could you please provide her phone number so I can validate it, send the message, and update the read status?
20Observation: [User] I don’t have Alice’s phone number available, but her user ID is Alice Chan (USR1). Can you go ahead and send the update to her using that, and mark her unread message from Brandon as “read”?
Action: Function(name="get_user_by_id", arguments={"user_id": "USR1"})
21Observation: [Tool Result] {’success’: True, ’data’: {’_id’: ’USR1’, ’phone_number’: ’+15551234567’, ’name’: ’Alice Chan’, ’profile_info’: ’admin’}}
Action: Function(name="send_message", arguments={"sender_id": "USR2", "receiver_phone_number": "+15551234567", "content": "Update: re-sent Gabby’s invite and delivery confirmed."})
22Observation: [Tool Result] {’success’: True, ’message’: ’Message sent successfully.’}
Action: Function(name="get_messages_by_phone_number", arguments={"phone_number": "+15551234567"})
23Observation: [Tool Result] {’success’: True, ’data’: [{’message_id’: ’MSG001’, ’sender_id’: ’USR1’, ’receiver_id’: ’USR2’, ’receiver_phone_number’: ’+15559874566’, ’content’: ’Hey Brandon, can you send the report by Friday?’, ’timestamp’: ’2024-02-17T14:38:05Z’, ’delivery_status’: ’delivered’, ’read_status’: ’read’}, {’message_id’: ’MSG002’, ’sender_id’: ’USR2’, ’receiver_id’: ’USR1’, ’receiver_phone_number’: ’+15551234567’, ’content’: "Hi Alice, yes I’ll have it done tomorrow.", ’timestamp’: ’2024-02-17T15:00:51Z’, ’delivery_status’: ’delivered’, ’read_status’: ’unread’}, {’message_id’: ’MSG006’, ’sender_id’: ’USR5’, ’receiver_id’: ’’, ’receiver_phone_number’: ’+15551234567’, ’content’: ’Alice, your settings have been updated securely.’, ’timestamp’: ’2024-06-01T10:02:46Z’, ’delivery_status’: ’sent’, ’read_status’: ’unread’}, {’message_id’: ’MSG007’, ’sender_id’: ’USR1’, ’receiver_id’: ’USR5’, ’receiver_phone_number’: ’+812345678901’, ’content’: ’Emi, the new version is available for testing.’, ’timestamp’: ’2024-05-15T07:17:22Z’, ’delivery_status’: ’delivered’, ’read_status’: ’read’}, {’message_id’: ’b2e86264-96cb-40c0-b7dd-39091b185d15’, ’sender_id’: ’USR2’, ’receiver_id’: ’USR1’, ’receiver_phone_number’: ’+15551234567’, ’content’: ’Update: re-sent Gabby’s invite and delivery confirmed.’, ’timestamp’: ’1763054573.840822’, ’delivery_status’: ’sent’, ’read_status’: ’unread’}]}
Action: Function(name="mark_message_as_read", arguments={"message_id": "MSG002"})
24Observation: [Tool Result] {’success’: True, ’message’: ’Message marked as read.’}
Action: The message to Alice Chan has been successfully sent: "Update: re-sent Gabby’s invite and delivery confirmed." Additionally, Alice’s previously unread message from Brandon (ID: ‘MSG002‘) has been marked as read. All actions completed! Let me know if you need further assistance.
Table 14: A trajectory example of Conversation setting (The reasoning process <think>…</think> is ignored).