Synthetic data generation and inference pipeline for deep-research / multi-hop QA tasks: BrowseComp-en, BrowseComp-zh, HotpotQA-MuSiQue, and xBench.
The agent uses a Hierarchical Memory System — search, visit, expand, fold, and commit tools — to conduct multi-step web research while keeping the context window manageable.
| Type | Name | Hugging Face |
|---|---|---|
| Model | LightThinker-Plus-Qwen3-30B-A3B | zjunlp/LightThinker-Plus-Qwen3-30B-A3B |
| SFT Data | Agentic Reasoning | zjunlp/LightThinker-Plus-AgenticReasoning-SFT |
Install dependencies:
bash setup.shTwo external services are required for web search and page reading:
| Service | Purpose | Sign-up |
|---|---|---|
| Serper | Web search & Google Scholar | SERPER_KEY_ID |
| Jina AI | Webpage content parsing | JINA_API_KEYS |
The pipeline is controlled entirely through environment variables loaded from a .env file.
Two commit-safe template files are provided — copy the appropriate one before running:
# For synthetic data generation:
cp .env.synthetic.example .env
# For inference on a trained model:
cp .env.infer.example .envThen open .env and fill in your credentials.
| Variable | Description |
|---|---|
SERPER_KEY_ID |
Serper API key |
JINA_API_KEYS |
Jina AI API key |
REASONING_SK |
API key for the main reasoning model |
REASONING_ENDPOINT |
OpenAI-compatible base URL for the main model |
MODEL_NAME |
Model name as served by the endpoint |
| Variable | Default | Description |
|---|---|---|
MAX_ROUNDS |
100 |
Maximum agent turns per sample |
MAX_REASON_TOKENS |
110592 |
Token budget before forcing a final answer |
LOGGER_MODEL_NAME |
"" |
Logger / summarizer model name |
LOGGER_MODEL_URL |
"" |
Logger model API endpoint |
LOGGER_MODEL_API_KEY |
"" |
Logger model API key |
EVALUATE_MODEL_NAME |
"" |
Judge model for evaluation |
EVALUATE_API_BASE |
"" |
Judge model API endpoint |
EVALUATE_API_KEY |
"" |
Judge model API key |
TOKENIZER_PATH |
./core/tasks/tokenizer/Qwen3-30B-A3B-Thinking-2507 |
HuggingFace tokenizer for token counting (falls back to tiktoken if unavailable) |
Edit the user-config section at the top of scripts/synthetic.sh, then:
bash scripts/synthetic.shTo override the model or run multiple iterations:
bash scripts/synthetic.sh \
--ports 8210 \
--model_name your-model-name \
--max_workers 10 \
--iterations 3Key script variables:
| Variable | Description |
|---|---|
EXPERIMENT_NAME |
Output directory name under results-synthetic/ |
TASK_NAME |
Dataset task: DeepResearch, BC_en, BC_zh, xbench |
DATASET_PATH |
Path to the input JSONL dataset |
MAIN_SYS_PROMPT |
Key into PROMPTS_MAP for the main agent |
SEARCH_LOGGER_SYS_PROMPT |
Key for the search-logger agent |
VISIT_LOGGER_SYS_PROMPT |
Key for the visit-logger agent |
REACT_MODE |
fn_call (recommended) or prompt |
Output is written to results-synthetic/<EXPERIMENT_NAME>/iter<N>_raw_outputs.jsonl.
Edit the model settings in .env, then:
bash scripts/inference.shThe inference script uses a single system_prompt (no dual-model logger):
bash scripts/inference.sh \
--ports 8210 \
--model_name your-model-name \
--max_workers 10Output is written to results-inference/<EXPERIMENT_NAME>/iter<N>_raw_outputs.jsonl.
Convert raw interaction trajectories into SFT training data:
python process/data_filter.py \
--input results-synthetic/my-run/iter1_raw_outputs.jsonl \
--output curated/train_data.jsonMultiple input files can be provided:
python process/data_filter.py \
--input results-synthetic/run1/iter1_raw_outputs.jsonl \
results-synthetic/run2/iter1_raw_outputs.jsonl \
--output curated/train_data.jsonThe script applies the following quality filters:
- Short simple trajectories (< 3 rounds with no memory tools) are dropped
- Memory logic validation: trajectories using
expand/fold/commitmust satisfyexpand → foldordering with no duplicates - Length filter: trajectories exceeding 60 rounds are dropped
Output report columns:
| Column | Description |
|---|---|
Memory Kept |
Valid trajectories using expand/fold/commit |
Simple Kept |
Valid simple trajectories (no memory tools) |
Drop (No EF Mix) |
Missing required expand+fold+commit combination |
Drop (Fold Error) |
fold called without a preceding expand |
Drop (Jitter) |
Consecutive duplicate memory operations |
Drop (Too Long) |
Exceeded 60-round threshold |
# fn_call mode (recommended for synthetic data generation)
python main.py \
--task_name DeepResearch \
--model_name your-model \
--base_url http://localhost:8000/v1 \
--dataset_path datasets/browsecomp_en.jsonl \
--toolsets DEEPRESEARCH \
--react_mode fn_call \
--main_system_prompt synthetic_main_agent \
--search_logger_system_prompt search_logger \
--visit_logger_system_prompt visit_logger \
--output_dir results/ \
--output_base_name iter1 \
--max_workers 10
# prompt mode (for inference with a single system prompt)
python main.py \
--task_name DeepResearch \
--model_name your-model \
--base_url http://localhost:8000/v1 \
--dataset_path datasets/browsecomp_en.jsonl \
--toolsets DEEPRESEARCH \
--react_mode prompt \
--system_prompt infer_agent \
--output_dir results/ \
--output_base_name iter1Supported --task_name values: DeepResearch, BC_en, BC_zh, xbench
Supported --toolsets values: DEEPRESEARCH, DEEPRESEARCH_BASE
Place dataset files under datasets/. Expected JSONL format — one record per line with at minimum id, question, and answer fields.