Skip to content

feat: add end-to-end synthetic conversational tool-use generation and rollouts - #2111

Merged
bxyu-nvidia merged 31 commits into
mainfrom
jk/synthetic-conversational-tool-use
Aug 8, 2026
Merged

bxyu-nvidia merged 31 commits into
mainfrom
jk/synthetic-conversational-tool-use

Conversation

@jkyi-nvidia

@jkyi-nvidia jkyi-nvidia commented Jul 22, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Adds a Gym-native pipeline for generating and evaluating long-horizon synthetic conversational tool-use tasks.

The pipeline is split into five composable components:

  • conversational_tool_use_domain_generation: generates domain candidates.
  • conversational_tool_use_policy_tool_generation: generates, refines, validates, and judges domain policies and tool schemas.
  • conversational_tool_use_scenario_generation: generates customer scenarios for accepted policy/tool artifacts.
  • conversational_tool_use_agent: runs the policy-model conversation loop.
  • conversational_tool_use_simulation: simulates users and tool results, maintains session state, and verifies completed trajectories.

Each component uses standard Gym agents, resource servers, model-server references, rollout JSONL, and explicit materialization steps. This PR does not change nemo_gym core infrastructure.

Design

Generation pipeline

The generation stages are independent Gym agents connected through typed rollout artifacts:

domain rollouts
  -> policy/tool inputs and rollouts
  -> scenario inputs and rollouts
  -> conversational tool-use inputs
  -> policy agent + rollout simulation

Each rollout preserves the generated artifact, source lineage, model requests and responses, parsing outcomes, and validation or judgment results. Materializers produce stable IDs and the exact row contract required by the next stage.

The pipeline supports general and proactive profiles. Prompt assets, schemas, golden policy/tool references, checked-in examples, and package-specific configuration live with the component that owns them.

Conversational rollout environment

The policy agent supports multi-turn assistant messages, sequential and parallel function calls, prefilled histories, transfers, and bounded policy-model steps. A normal assistant message does not terminate the rollout: the agent asks the simulator for the next user turn and continues until a stop condition, transfer, incomplete response, verification failure, or step limit.

The simulation resource server owns:

  • per-rollout policy, tools, scenario, and conversation state;
  • user and tool-result simulation;
  • generated tool routing and JSON Schema validation;
  • message-level and final-conversation verification;
  • retryable provider-failure handling and failure labels;
  • cleanup of completed or abandoned sessions.

Policy, user-simulator, tool-simulator, and judge roles are separate model-server references and can use different models or providers.

Transcript and data contracts

The returned Gym row has two aligned views:

  • responses_create_params and response contain the policy-visible Responses API transcript;
  • result.trajectory contains simulator-native user, agent, tool, terminal, and verification state.

The agent validates that the materialized system prompt and model-visible tools match the top-level policy and simulator tool definitions before starting a rollout.

Provider output order is preserved when a response contains assistant content and function calls. Parallel tool results are appended only after all selected response items. Each decomposed trajectory message records its source response_id and response_output_index; the complete raw response is stored once rather than duplicated on every message.

Transient seeding, generation, and verification failures use Gym's failure-sidecar contract and are excluded from scored rollout JSONL. Semantic failures remain verified rollout results with synchronized reward, invalid-reason, failure-label, and judge-diagnostic fields.

Dataset preparation

The resource-server package includes builders for checked-in examples, full general and proactive datasets, combined datasets, parallel-tool-call variants, and deterministic balanced sampling.

Builders reject malformed or incomplete tools, invalid JSON Schemas, malformed scenarios, duplicate tool names, and model-template leakage. Full generated datasets, reports, and rollout outputs are intentionally not checked in.

Validation

Automated validation

  • 68 passed: resources_servers/conversational_tool_use_simulation test suite.
  • 34 passed: responses_api_agents/conversational_tool_use/simulation test suite.
  • Ruff and repository pre-commit hooks pass.

Live rollout validation

  • A hosted-inference rollout completed through Gym's inference_provider using DeepSeek V4 Pro as the policy and Nemotron 3 Ultra as simulator and judge.
  • The reward-1 trajectory exercised parallel function calls and confirmed converted output ordering as reasoning -> message -> function_call(s).

End-to-end NeMo RL validation

The materialized conversational tool-use dataset was used to train NVIDIA-Nemotron-3-Nano-30B-A3B-BF16 through Step 30 with multi-turn GRPO. Steps 10, 20, and 30 were converted from DCP to Hugging Face format and evaluated alongside the base checkpoint on TAU2 and TAU3 BM25 artificial analysis.

Checkpoint Airline Retail Telecom TAU2 average TAU3 BM25-AA
Base 33.50% 62.94% 44.08% 46.84% 4.95%
Step 10 36.00% 62.39% 43.53% 47.31% 4.33%
Step 20 36.00% 63.93% 44.08% 48.00% 5.36%
Step 30 35.50% 62.94% 46.49% 48.31% 4.54%

TAU2 average is the arithmetic mean across airline, retail, and telecom. This run validates the complete downstream path from dataset materialization through rollout collection, verification, training, checkpoint conversion, and Gym evaluation. TAU2 average increased from 46.84% at base to 48.31% at Step 30.

Co-authored-by: Jonathan Li <jonli@nvidia.com>
Signed-off-by: Junkeun Yi <jkyi@nvidia.com>
@copy-pr-bot

copy-pr-bot Bot commented Jul 22, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

Signed-off-by: Junkeun Yi <jkyi@nvidia.com>
Signed-off-by: Junkeun Yi <jkyi@nvidia.com>
Signed-off-by: Junkeun Yi <jkyi@nvidia.com>
Signed-off-by: Junkeun Yi <jkyi@nvidia.com>
@jkyi-nvidia
jkyi-nvidia marked this pull request as ready for review July 24, 2026 17:18
Signed-off-by: Junkeun Yi <jkyi@nvidia.com>
@jkyi-nvidia
jkyi-nvidia requested a review from a team as a code owner July 24, 2026 21:03
Signed-off-by: Junkeun Yi <jkyi@nvidia.com>
Signed-off-by: Junkeun Yi <jkyi@nvidia.com>
Signed-off-by: Junkeun Yi <jkyi@nvidia.com>
Signed-off-by: Junkeun Yi <jkyi@nvidia.com>
@github-actions github-actions Bot added the sla:triage-overdue Review assignment is over the one-business-day SLA label Jul 27, 2026
@bxyu-nvidia
bxyu-nvidia merged commit de2dbda into main Aug 8, 2026
9 checks passed
@bxyu-nvidia
bxyu-nvidia deleted the jk/synthetic-conversational-tool-use branch August 8, 2026 01:57
dims pushed a commit to dims/Nemo-Gym that referenced this pull request Oct 14, 2026
…train judge JSON, report mask_sample (NVIDIA-NeMo#4568)

## What does this PR do?

The nested conversational tool-use environment
(`resources_servers/conversational_tool_use_simulation` with the
`conversational_tool_use/simulation` agent) now starts from a source
checkout, and its rollouts are graded and
masked correctly. Before this PR its stack never came up: every model
server bound to the agent crashed during
startup. Behind that crash were two more defects, which this PR also
fixes. The trajectory judge often returned JSON
that failed validation, and the server's mask decision never reached the
`mask_sample` field that Gym and trainers read.

Fixes NVBug [6847461](https://nvbugspro.nvidia.com/bug/6847461). Closes
NVIDIA-NeMo#4497.

### Cause

1. **Startup crash.** `ServerClient.assistant_message_header`
(`nemo_gym/server_utils.py:825`, added by NVIDIA-NeMo#3711)
imports `responses_api_agents.{harness}` with the raw config key. Nested
harnesses are keyed by directory path,
so `conversational_tool_use/simulation` raises `ModuleNotFoundError` in
`policy_model` setup
(`base_responses_api_model.py:286`). The venv blocker in the same NVBug
(a pin to `nemo-gym==<version>`) was
   already fixed by NVIDIA-NeMo#4464.
2. **Unparseable judge output.** The judge prompt contains
`Evaluation.model_json_schema()` as text, and the reply
is parsed with `model_validate_json`, with no decoding constraint (since
NVIDIA-NeMo#2111). With the QA model (temperature 1.0), the
step-0 judgement failed all 3 attempts in 3 of 3 runs. Replies either
wrapped the instance in
`{"properties": {...}}` or left a string unquoted, so the row got
`verification_generation_error`.
3. **Mask dropped.** NVIDIA-NeMo#2111 reported the mask decision only as
`instance_config.mask_sample`. NVIDIA-NeMo#2611 later made
`BaseVerifyResponse.mask_sample` the contract. This environment was
never migrated, so simulator, tool, and judge
   failures reached rollouts with `mask_sample: false` and reward 0.

### Fix

1. Map `/` to `.` before importing the harness package.
2. When `judge_responses_create_params.text` is not configured, the
judge requests Responses
`text.format` (`json_schema`) for `Evaluation`, or for
`UserAgentEnvironmentEvaluation` in combined mode. A
configured `text` still wins. `openai_model` forwards it to the
provider; the chat converter maps it to
   `response_format`.
3. The resources server's `/verify` and the agent's transient-failure
response set top-level `mask_sample`.
`instance_config` is removed, and the checked-in
`example_rollouts.jsonl` is migrated. The README states the
   masking rule.

Rejected: catching `ImportError` in `assistant_message_header`. It would
hide a declared header from any nested
harness, and the new test fails on it.

### Before the fix

Gym `4a101eeb5` (main), QA case `conversational_tool_use_simulation`:

```bash
./run.sh test conversational_tool_use_simulation --ref 4a101ee --no-ai-log
```

```text
nemo_gym/base_responses_api_model.py", line 286, in setup_webserver
    assistant_message_header=self.server_client.assistant_message_header(self.config.name),
nemo_gym/server_utils.py", line 825, in assistant_message_header
ModuleNotFoundError: No module named 'responses_api_agents.conversational_tool_use/simulation'
RuntimeError: Process `policy_model` finished unexpectedly!
  ERROR (30.2s): StackStartupError: `gym env start` exited with code 1 during child startup
conversational_tool_use_simulation: 0/3 passed in 90.6s
```

With only the import fixed (intermediate commit), the stack started but
the judge failed to parse:

```text
FAIL (78.7s): original conversation has no generation or verification failures: expected [], got ['verification_generation_error']
generation_error = 2 validation errors for Evaluation
success
  Field required [type=missing, input_value={'properties': {'success'...tion', 'type': 'object'}, input_type=dict]
```

### After the fix

Gym `29d57f66c` (this branch before a rebase onto `977583d3d`; the one
new main commit touches no file in this diff), same command:

```bash
./run.sh test conversational_tool_use_simulation --ref 29d57f66c --no-ai-log
```

```text
PASS   373.9s  the canonical one-example conversation uses live roles and verifies session disposal and grading gates
PASS    79.1s  enable_llm_judge=false scores a complete conversation 1.0 without calling the judge
PASS    49.8s  complete_trajectory_combined_evaluation judges the whole conversation once with per-role verdicts
conversational_tool_use_simulation: 3/3 passed in 502.8s
```

### Validation

- QA testcase `conversational_tool_use_simulation` (3 cases: canonical
message-level judge, `enable_llm_judge=false`,
`complete_trajectory_combined_evaluation`): 0/3 on main (stack startup),
3/3 on this branch. The canonical case also replays the recorded
conversation and checks the step limit (`excessive_length`, not masked),
`###TRANSFER###` with both scope labels, and rejection of malformed
history (25 direct resources-server calls).
- Judge-format probe against the QA endpoint, 6 calls each: prompt-only
schema gave 5/6 valid replies, `text.format`
  gave 6/6.
- After the rebase (head `a3a6ab1f4`), I reran `test_server_utils.py`
(173), `gym env test` for the server (78), and the agent tests (36): all
pass.
- `tests/unit_tests/test_server_utils.py`: 173 passed. The new
`test_model_header_imports_nested_harness_key` fails on
  main, and also fails on a catch-`ImportError` fix.
- `gym env test --resources-server conversational_tool_use_simulation`:
78 passed. New judge structured-output
tests cover both judge paths and a configured `text`. 8 mask assertions
now check top-level `mask_sample`, and the
  example-rollout contract test checks the masking rule.
- `responses_api_agents/conversational_tool_use/simulation/tests`: 36
passed. 4 mask assertions moved to `mask_sample`.
- `tests/unit_tests` (Mac): same 43 pre-existing failures as main, plus
the new passing test. In one full
run,
`test_slurm_script.py::…multi_node_chain_survives_symmetric_run_entrypoint`
also failed (a `bash` subprocess
with a 10 s timeout). It passes 3/3 alone on both trees and 226/226 as a
file, and this diff does not touch it.
- `pre-commit run --all-files`: passed, no changes
- Rollout evidence: the QA case above runs live rollouts. The policy,
simulator, and judge all use
  `nvidia/nvidia/nemotron-3.5-super-vl-preview` through `openai_model`.

### Compatibility

- Consumers that read `instance_config.mask_sample` from this
environment, such as older NeMo RL, no longer get a
mask. They should read top-level `mask_sample`, like every other
environment.
- The judge request now includes `text.format`. A provider that rejects
`json_schema` structured output can turn it
  off by setting `judge_responses_create_params.text.format.type=text`.
- With a live judge, rows that it scores as user or tool failures are
now masked. Before this PR they counted as
  reward-0 policy samples.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Signed-off-by: qiaochuz <qiaochuz@nvidia.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

sla:triage-overdue Review assignment is over the one-business-day SLA

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants