File size: 13,266 Bytes
089301a
 
 
e156d58
 
 
 
089301a
 
 
 
e156d58
 
 
 
7768979
089301a
 
 
8f79670
1b3d490
9280de1
1b3d490
69c58b5
 
e156d58
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7e94fa1
e156d58
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
bd08824
 
 
 
 
 
 
 
e156d58
 
 
 
bd08824
e156d58
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
---
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 training pipeline](flow_charts.png)

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)