Text Generation
Transformers
Safetensors
Chinese
English
qwen3
causal-lm
qwen
finetune
user-simulator
role-play
sales
dpo
conversational
text-generation-inference
Instructions to use MultiSense/CustomerLM with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use MultiSense/CustomerLM with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-generation", model="MultiSense/CustomerLM") messages = [ {"role": "user", "content": "Who are you?"}, ] pipe(messages)# Load model directly from transformers import AutoTokenizer, AutoModelForCausalLM tokenizer = AutoTokenizer.from_pretrained("MultiSense/CustomerLM") model = AutoModelForCausalLM.from_pretrained("MultiSense/CustomerLM", device_map="auto") messages = [ {"role": "user", "content": "Who are you?"}, ] inputs = tokenizer.apply_chat_template( messages, add_generation_prompt=True, tokenize=True, return_dict=True, return_tensors="pt", ).to(model.device) outputs = model.generate(**inputs, max_new_tokens=40) print(tokenizer.decode(outputs[0][inputs["input_ids"].shape[-1]:])) - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- vLLM
How to use MultiSense/CustomerLM with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "MultiSense/CustomerLM" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "MultiSense/CustomerLM", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }'Use Docker
docker model run hf.co/MultiSense/CustomerLM
- SGLang
How to use MultiSense/CustomerLM with SGLang:
Install from pip and serve model
# Install SGLang from pip: pip install sglang # Start the SGLang server: python3 -m sglang.launch_server \ --model-path "MultiSense/CustomerLM" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "MultiSense/CustomerLM", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }'Use Docker images
docker run --gpus all \ --shm-size 32g \ -p 30000:30000 \ -v ~/.cache/huggingface:/root/.cache/huggingface \ --env "HF_TOKEN=<secret>" \ --ipc=host \ lmsysorg/sglang:latest \ python3 -m sglang.launch_server \ --model-path "MultiSense/CustomerLM" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "MultiSense/CustomerLM", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }' - Docker Model Runner
How to use MultiSense/CustomerLM with Docker Model Runner:
docker model run hf.co/MultiSense/CustomerLM
| license: apache-2.0 | |
| library_name: transformers | |
| pipeline_tag: text-generation | |
| language: | |
| - zh | |
| - en | |
| tags: | |
| - causal-lm | |
| - qwen | |
| - finetune | |
| - user-simulator | |
| - role-play | |
| - sales | |
| - dpo | |
| base_model: Qwen/Qwen3-8B-Instruct | |
| # CustomerLM | |
| **Our work has been acepted by EMNLP 2026** | |
| β **If you find this project helpful, please give us a star on [GitHub](https://github.com/Bairong-Xdynamics/Benchmarking-LLM-Realistic-Selling-Skill/)!** It means a lot to us. | |
| Github: https://github.com/Bairong-Xdynamics/Benchmarking-LLM-Realistic-Selling-Skill/ | |
| **CustomerLM** is a fine-tuned large language model based on Qwen, trained to play the **customer** side of a realistic sales conversation. It is the user simulator of the [SalesLLM benchmark](https://github.com/Bairong-Xdynamics/Benchmarking-LLM-Realistic-Selling-Skill). | |
| Most evaluations of "can an LLM sell?" put a general-purpose model (e.g. GPT-4o) on the customer side. That is the weak link: general instruct models are trained to be helpful, so when they are told to role-play a buyer they drift into *assisting* the salesperson β volunteering objections' answers, summarizing benefits, even pitching the product back. We call this **role inversion**, and it happens in **17.44%** of GPT-4o-simulated dialogues. A user simulator that behaves like a salesperson makes every downstream sales score unreliable. | |
| CustomerLM is trained specifically to stay in character as a buyer: skeptical when the persona says skeptical, terse, self-interested, and willing to walk away. It cuts role inversion to **8.8%** while producing responses measurably closer to real human customers. | |
| | | | | |
| | :--- | :--- | | |
| | **Base model** | Qwen (see `config.json` for the exact checkpoint) | | |
| | **Training** | SFT β DPO | | |
| | **Training data** | 8,284 crowdworker-involved real-world sales dialogues | | |
| | **Languages** | Chinese, English | | |
| | **Role** | User simulator (customer), **not** an assistant | | |
| | **License** | Apache 2.0 | | |
| --- | |
| ## Intended Use | |
| **Use it for:** | |
| - Driving the customer side of multi-turn sales dialogue simulation, so you can benchmark or stress-test a sales agent. | |
| - Generating synthetic sales conversations for training or analysis. | |
| - Any role-play evaluation harness that needs a non-compliant, in-character human counterpart. | |
| **Do not use it for:** | |
| - Serving end users as an assistant. It is trained to behave like a customer β it will ask questions, push back, and refuse rather than help. | |
| - Producing factual claims about real products. It speaks from a persona, not a knowledge base. | |
| - Representing real people. Personas are synthetic and are not models of any individual. | |
| --- | |
| ## How It Works | |
| CustomerLM takes the **customer persona as its system prompt** and the **salesperson's turns as user messages**. Its own replies are the customer's turns. Note the role mapping β it is inverted relative to a normal assistant deployment: | |
| | Chat role | Who it is | | |
| | :--- | :--- | | |
| | `system` | The customer persona (difficulty, buy-inclination, pain points, decision factors) | | |
| | `user` | The **salesperson** speaking to the customer | | |
| | `assistant` | **CustomerLM** β the customer's reply | | |
| ### System prompt format | |
| CustomerLM was fine-tuned on this layout. Keeping it maximises fidelity; deviating from it degrades persona adherence. | |
| ``` | |
| - Difficulty level: <easy | medium | hard | very_hard> | |
| - Buy-inclination score: <0.0 - 1.0> | |
| - Persona: <one-paragraph description of the buyer's stance> | |
| CUSTOMER_INFORMATION (private): | |
| Basic information | |
| {"age_group": "...", "gender": "...", "location": "...", "occupation": "..."} | |
| Motivation | |
| <what they are trying to achieve> | |
| Pain points | |
| <what worries them> | |
| Decision factors | |
| <what makes them say yes> | |
| Communication preference | |
| <channels and tone they like> | |
| Language | |
| <Chinese | English> | |
| ``` | |
| `Difficulty level` and `Buy-inclination score` are the difficulty knob and should move together: `easy β 0.8β1.0`, `medium β 0.5β0.7`, `hard β 0.2β0.4`, `very_hard β 0.0β0.1`. | |
| --- | |
| ## Usage | |
| ### Quick start (transformers) | |
| ```python | |
| from transformers import AutoModelForCausalLM, AutoTokenizer | |
| model_id = "MultiSense/CustomerLM" | |
| tok = AutoTokenizer.from_pretrained(model_id) | |
| model = AutoModelForCausalLM.from_pretrained(model_id, torch_dtype="auto", device_map="auto") | |
| persona = """- Difficulty level: hard | |
| - Buy-inclination score: 0.3 | |
| - Persona: Skeptical, price-sensitive buyer who needs concrete evidence before committing. | |
| CUSTOMER_INFORMATION (private): | |
| Basic information | |
| {"age_group": "35-44", "gender": "female", "location": "Boston", "occupation": "software engineer"} | |
| Motivation | |
| Wants a quieter commute and better focus while working from cafes. | |
| Pain points | |
| Burned by cheap headphones before; suspicious of marketing claims. | |
| Decision factors | |
| Measured noise-cancellation performance, comfort, warranty, price under $300. | |
| Communication preference | |
| Direct, fact-dense answers; concrete numbers over adjectives. | |
| Language | |
| English""" | |
| messages = [ | |
| {"role": "system", "content": persona}, | |
| # the SALESPERSON speaks as `user` | |
| {"role": "user", "content": "Hi! Are you looking for noise-cancelling headphones today?"}, | |
| ] | |
| inputs = tok.apply_chat_template(messages, add_generation_prompt=True, return_tensors="pt").to(model.device) | |
| out = model.generate(inputs, max_new_tokens=256, temperature=0.8, top_p=0.99, do_sample=True) | |
| print(tok.decode(out[0][inputs.shape[-1]:], skip_special_tokens=True)) | |
| ``` | |
| ### Serving with vLLM | |
| The SalesLLM harness talks to CustomerLM over an OpenAI-compatible endpoint, so vLLM is the recommended way to run it: | |
| ```bash | |
| vllm serve MultiSense/CustomerLM \ | |
| --served-model-name CustomerLM \ | |
| --port 8000 | |
| ``` | |
| ### Use as the SalesLLM user simulator | |
| Point the benchmark's `--user_*` flags at your vLLM server. The harness handles the role mapping, the persona injection, and the turn loop for you: | |
| ```bash | |
| python salesllm/salesllm_evaluation.py \ | |
| --assistant_model_name "MODEL_UNDER_TEST" \ | |
| --assistant_API_end_point "https://YOUR_ASSISTANT_ENDPOINT" \ | |
| --assistant_API_key "YOUR_KEY" \ | |
| --user_model_name "CustomerLM" \ | |
| --user_API_end_point "http://localhost:8000/v1" \ | |
| --user_API_key "EMPTY" \ | |
| --user_api_type "other" \ | |
| --execution_mode "concurrent" \ | |
| --round_num 20 \ | |
| --input_file "data/benchmark/conversations_1000_zh.jsonl" \ | |
| --output_dir "./results/zh/" \ | |
| --language "zh" | |
| ``` | |
| ### Recommended decoding | |
| | Parameter | Value | | |
| | :--- | :--- | | |
| | `temperature` | 0.8 | | |
| | `top_p` | 0.99 | | |
| | `max_tokens` | 2048 | | |
| Sampling matters here. Greedy decoding makes the customer flat and repetitive, which suppresses the natural objection variety the model was trained to produce. | |
| --- | |
| ## Training | |
|  | |
| CustomerLM is trained in two stages on **8,284 crowdworker-involved real-world sales dialogues**. | |
| ### Stage 1 β SFT (learn to talk like a customer) | |
| Real sales conversations are collected and pre-processed, then the base Qwen model is supervised-fine-tuned on the **customer turns only**, conditioned on the persona reconstructed from that conversation. This produces the SFT model, which already speaks in a customer register but still inverts roles under pressure. | |
| ### Stage 2 β DPO (learn *not* to behave like a salesperson) | |
| SFT alone does not remove role inversion, because the failure is behavioural rather than stylistic. So we build preference pairs that target it directly: | |
| 1. The SFT model is paired with a sales model to simulate dialogues from generated scripts. | |
| 2. An **LLM judge** labels each simulated customer turn as **assistant-like** (the failure: helpful, pitching, summarizing benefits) or **user-like** (the target: in-character buyer behaviour). | |
| 3. Assistant-like turns are sent through **GPT-4o + human correction** to produce the user-like counterpart of the same turn. | |
| 4. Each (assistant-like, corrected user-like) pair becomes a DPO pair β rejected and chosen respectively. | |
| 5. **DPO** on these pairs yields the final model. | |
| The key design choice is that the preference signal is *contrastive on the exact failure mode*. Both sides of every pair respond to the same context, so the gradient isolates "stop being an assistant" from everything else the model already learned in SFT. | |
| --- | |
| ## Evaluation | |
| ### User simulation fidelity | |
| Held-out set of human-annotated real conversations (118 ZH, 150 EN). Reference-based metrics compare the simulated customer turn against the real human customer turn; **Role Inversion** is the share of dialogues where the simulated customer slips into salesperson behaviour (lower is better). | |
| | **User Model** | **BLEU-4** | **ROUGE-1** | **ROUGE-2** | **ROUGE-L** | **Sem. Sim.** | **Role Inversion (%)** | | |
| | :--- | :---: | :---: | :---: | :---: | :---: | :---: | | |
| | GPT-4o | 0.10 | 0.08 | 0.02 | 0.07 | 0.57 | 17.44 | | |
| | UserLM | 0.06 | 0.08 | 0.01 | 0.06 | 0.50 | 21.55 | | |
| | USP | 0.08 | 0.09 | 0.01 | 0.08 | 0.52 | 18.76 | | |
| | **CustomerLM (Ours)** | **0.12** | **0.11** | **0.03** | **0.10** | **0.59** | **8.8** | | |
| CustomerLM wins on every metric. The headline result is role inversion: **8.8% vs. 17.44% for GPT-4o**, roughly a 2Γ reduction, achieved by a much smaller model than the GPT-4o baseline it beats. | |
| Read the n-gram scores in context β BLEU-4 of 0.12 is low in absolute terms because there is no single correct next customer turn. What matters is the *relative* ordering against baselines on identical references, plus semantic similarity (0.59), which is the more meaningful signal for open-ended dialogue. | |
| ### Effect on downstream benchmark scores | |
| Swapping GPT-4o for CustomerLM changes the measured selling skill of the models under test, which is the practical reason the simulator quality matters. SalesLLM Score (0β10) on 1,000 ZH / 805 EN scripts: | |
| | **Assistant Model** | **ZH (GPT-4o user)** | **ZH (CustomerLM user)** | **EN (GPT-4o user)** | **EN (CustomerLM user)** | | |
| | :--- | :---: | :---: | :---: | :---: | | |
| | Doubao-32K | 6.07 | 6.89 | 6.31 | 5.48 | | |
| | Qwen-max | 6.02 | 5.55 | 5.97 | 5.56 | | |
| | GPT-4o | 5.72 | 6.15 | 5.53 | 5.19 | | |
| | GLM4-0414-9B | 6.01 | **7.14** | 5.92 | 5.55 | | |
| | GLM4.6 | 6.74 | 6.86 | 5.64 | 5.32 | | |
| | Qwen3-8B | 5.40 | 5.64 | 5.56 | 5.79 | | |
| | Qwen3-32B | 5.81 | 5.79 | 6.13 | 5.62 | | |
| | Qwen3-72B | 6.06 | 5.70 | 5.76 | 5.63 | | |
| Scores are **not comparable across user models** β only compare within a column. The rankings genuinely reorder between the two simulators, so a leaderboard built on a GPT-4o customer is measuring something different from one built on CustomerLM. | |
| ### Reproducing these numbers | |
| ```bash | |
| # Simulation fidelity (BLEU / ROUGE / semantic similarity) | |
| python salesllm/user_likness_eval.py \ | |
| --model_name "CustomerLM" \ | |
| --api_end_point "http://localhost:8000/v1" --api_key "EMPTY" --api_type "other" \ | |
| --input_files <held_out_conversations>.jsonl \ | |
| --output_file user_likeness_results.jsonl --language zh | |
| # Role inversion rate (LLM judge over generated dialogues) | |
| python salesllm/reverse_role_eval.py \ | |
| --model_name "<judge_model>" \ | |
| --api_end_point "<judge_endpoint>" --api_key "<judge_key>" \ | |
| --input_files ./results/zh/*.jsonl \ | |
| --output_dir ./reverse_role_results | |
| ``` | |
| --- | |
| ## Limitations and Risks | |
| - **Not an assistant.** Deployed as a chatbot it will act like a customer. This is by design and is not a defect to be prompted away. | |
| - **Format sensitivity.** Persona adherence degrades if the system prompt departs from the trained layout. Personas outside the trained distribution (B2B procurement, healthcare intake, etc.) are extrapolation. | |
| - **Domain coverage.** Training data is Financial Services and Consumer Goods. Other verticals are untested. | |
| - **Residual role inversion.** 8.8% is a large improvement, not a solution. Roughly one dialogue in eleven still exhibits the failure, so filter with `reverse_role_eval.py` when simulation quality is load-bearing. | |
| - **Reference metrics are weak instruments.** BLEU/ROUGE against a single human reference under-measure valid alternative customer responses; treat them as comparative, not absolute. | |
| - **Inherited bias.** Persona attributes include age, gender, location, and occupation, and the model may reproduce demographic stereotypes present in the base model and the source dialogues. Do not use generated personas to make claims about real demographic groups. | |
| - **Synthetic output.** Conversations produced by CustomerLM are simulations, not evidence of real customer preferences, and should not be used as market research. | |
| --- | |
| ## Citation | |
| ```bibtex | |
| @misc{su2026sellmoreplayless, | |
| title={Sell More, Play Less: Benchmarking LLM Realistic Selling Skill}, | |
| author={Xuanbo Su and Wenhao Hu and Le Zhan and Yuting Xie and Kailin Lyu and Kaijie Chen and Ziwei Li and Yeqiang Wang and Haibo Su and Yunzhang Chen and Ling Huang}, | |
| year={2026}, | |
| eprint={2604.07054}, | |
| archivePrefix={arXiv}, | |
| primaryClass={cs.CL}, | |
| url={https://arxiv.org/abs/2604.07054}, | |
| } | |
| ``` | |
| ## Related | |
| - π [Paper](https://arxiv.org/abs/2604.07054) | |
| - π [SalesLLM benchmark & code](https://github.com/Bairong-Xdynamics/Benchmarking-LLM-Realistic-Selling-Skill) | |
| - π€ [SaleIntent-BERT](https://huggingface.co/MultiSense/SaleIntent_bert) β buying-intent classifier used for outcome scoring (93.51% ZH, 92.94% EN) | |