wikillm

Paper · 2023-10-13 · cs.CL, cs.AI

"Kelly is a Warm Person, Joseph is a Role Model": Gender Biases in LLM-Generated Reference Letters

Measures gender bias in recommendation letters from ChatGPT and Alpaca, given a name and occupation (CLG) or a biography (CBG). Word choice is scored with odds ratios and WEAT, tone with formality, positivity and agency classifiers plus t-tests, and hallucinated sentences with NLI. Male-favouring tone gaps are significant; hallucinated text carries them further.

Algorithmic fairnessHallucination detectionWord embedding association testSentiment analysisfairnessbiastext-generationevaluationhallucination

arXivPDFCodeMarkdown14 min read

AI-compiled by claude-haiku-5-5 on 2026-11-01, not yet reviewed by a human. Every claim cites its source; check it when a detail matters.

In plain words

People increasingly ask chatbots to draft recommendation letters. This paper asks whether those drafts describe men and women differently. The authors give the chatbot either only a name, an age and a job, or a short Wikipedia-style life story, and then look at the letters that come back.

They check three things. First, which words the letters use for each gender, for example whether men get words about ability and leadership and women get words about warmth and home life. Second, the tone: whether men's letters are more formal, more positive, and more about achievement. Third, whether the made-up details that the model adds, beyond the facts it was given, carry the same differences. The answer in the paper is yes for both chatbots it tested. The gaps are small on some measures and large on others, and the letters are not safe to send without checking.

TL;DR

  • Asks ChatGPT (and, for the biography setting, Alpaca) to write recommendation letters, then compares letters for men and women. Author-reported.
  • Word-level gaps appear in a name-only setting (120 letters). Tone gaps (formality, positivity, agency) are significant in a biography setting with 6,028 ChatGPT letters and about 4,228 Alpaca letters.
  • Hallucinated sentences, found by an NLI model, show the same gaps. The paper calls the effect bias propagation and amplification.
  • Re-running the tone metrics needs three classifiers, a BERT agency model that is only on a Google Drive link, and care with the t-test (see Paper vs code).

Problem

The paper argues that model-generated reference letters might be used directly in professional settings, with risk if they carry bias §1. Social science work on human-written letters links certain word and tone patterns to lower outcomes for women, and the paper uses that work as its yardstick §2. Related work notes that prior NLG bias methods are unclear for closed API-based LLMs such as ChatGPT, and that little is known about whether hallucinations propagate or amplify biases §2.

Two settings are studied. In context-less generation (CLG), the model sees only a name, age and occupation, which the authors describe as a lens on underlying biases §3.1. In context-based generation (CBG), the model also sees a biography, which the authors describe as closer to real use §3.1.

Method

Overview. The study has three measurement families: word choice, language style and hallucinated content. Each is scored per letter or per sentence and then compared between male and female letters.

  1. Build prompts. CLG uses one template with a name, an age (20, 30, 40, 50 or 60) and an occupation. The paper uses 2 names (Kelly, Joseph) times 5 ages times 12 occupations, giving 120 letters §4.2.1 app. C.1 app. C.2. CBG starts from the WikiBias dataset §4.3.1. For each original biography the authors take two random paragraphs from the personal and career sections app. F.1, then produce one entry in the seed's gender and one in the other gender by swapping names and gendered pronouns app. F.1. The result, WikiBias-Aug, has 6,028 entries, 3,014 per gender, with equal male and female counts in each occupation app. D. Per-gender counts are: acting 567, artists 55, chefs 137, comedians 707, dancers 326, models 284, musicians 77, podcasters 215, sports 74, writers 572 app. D.

  2. Generate letters. ChatGPT writes one letter per CBG entry (6,028 letters) §4.3.2. Alpaca, Vicuna and StableLM were also tried; a letter counts as successful if it is non-empty, has no long run of text, and contains the word "recommend" app. B. Alpaca was kept (4,228 successful letters) and the other two were dropped §4.1 §4.3.2 app. B.

  3. Word choice, CLG. Each letter is split into whitespace tokens. Nine lexicon categories (ability, standout, leadership, masculine, feminine, agentic, communal, professional, personal) are matched against the tokens. For each category the paper computes an odds ratio of male-versus-female odds §4.2.2 app. F.3. Values above 1 mean the category is more likely in male letters.

  4. Word choice, CBG. Nouns and adjectives are extracted with spaCy §4.3.3. Each word gets an odds ratio of male-versus-female odds, and only words that appear at least a minimum number of times in both groups are kept §3.2.1 §4.3.3. The paper also reports WEAT scores between male and female names and career and family words §4.3.5 app. F.4. A positive WEAT score means female words sit closer to the female-stereotyped side.

  5. Language style, CBG. Each sentence gets a formality label, a sentiment label, and an agency label. The per-letter score is the percentage of sentences in the target category §3.2.2 §4.3.4. Formality uses a classifier tuned on GYAFC §4.3.4; sentiment uses a classifier tuned on SST-2 §4.3.4; agency uses a BERT classifier that the authors trained on ChatGPT-rephrased biographies, labelled as agentic or communal §4.3.4 app. F.2. Agentic language is defined as achievement-oriented and communal as social or service-oriented §2.

  6. Compare groups. Male and female score distributions are compared with a t-test. The paper writes the statistic as in eq. 1 §3.2.2. The t-test values are reported with significance stars at p < 0.1, p < 0.05 and p < 0.01 tab. 4.

    Pseudocode
    # Language-style bias statistic  [eq. 1] [§3.2.2]
    # Input:  scores_m, scores_f: one per-letter percentage per group
    # Output: t statistic, positive when male letters score higher
    
    def style_bias_statistic(scores_m, scores_f):
        # Difference of the two group means  [eq. 1]
        mean_gap = mean(scores_m) - mean(scores_f)
        # Denominator as written: sample variances over group sizes  [eq. 1]
        spread = sqrt(var(scores_m) / len(scores_m)
                      + var(scores_f) / len(scores_f))
        return mean_gap / spread
  7. Hallucination detection. Each letter is split into sentences. Each sentence, taken as a hypothesis, is checked against the biography, taken as the premise, with an NLI model §3.3 §4.4.1. A sentence that is not entailed is treated as hallucinated §3.3. The NLI model is RoBERTa-large fine-tuned on SNLI, MNLI, FEVER-NLI and ANLI R1 to R3 §4.4.1.

    Pseudocode
    # Context-sentence NLI check for hallucinated sentences  [§3.3]
    # Input:  biography (premise), letter (text)
    # Output: sentences the NLI model does not mark as entailed
    
    def find_hallucinated_sentences(biography, letter):
        # Split the letter into sentence hypotheses  [§3.3]
        hypotheses = split_sentences(letter)
        flagged = []
        for hypothesis in hypotheses:
            # Score the (biography, sentence) pair with the NLI model  [§3.3]
            probs = nli_model(biography, hypothesis)  # (ent, neu, con)
            # Flag unless the top label is entailment  [§3.3]
            if argmax(probs) != ENTAILMENT:
                flagged.append(hypothesis)
        return flagged
  8. Bias propagation and amplification. The three style scores are computed on the hallucinated sentences only and compared with the whole letter using one-sided t-tests §3.3. For male letters, the test asks whether the hallucinated part is higher on formality, positivity or agency. For female letters, it asks whether the hallucinated part is lower. Propagation means no significant difference. Amplification means a significant difference in the direction described above §3.3.

The figures are described in words only here. The hallucination pipeline is illustrated in fig. 1 and the WikiBias-Aug construction in fig. 2.

Hyperparameters

NameValueWhereNotes
ChatGPT modelgpt-3.5-turbo, chat completion APIapp. A generation_util.pyTemperature, top_p and n set to 1; no stop token. Alias, so the snapshot is not stated.
Open-model decoding (Alpaca, Vicuna, StableLM)max new tokens 512; repetition penalty 1.5; temperature 0.1; top_p 0.75; beams 2app. AChosen by tuning app. A. The code differs per model; see Paper vs code.
Success criterianon-empty; no long unbroken text; contains "recommend"app. BExact rule in code is in Paper vs code.
CLG grid2 names x 5 ages (20, 30, 40, 50, 60) x 12 occupations = 120 letters§4.2.1 app. C.1Names Kelly and Joseph.
CLG prompt"Generate a detailed reference letter for [name], a [age] year old [gender] [occupation]."app. C.2 generate_clg.pyPaper shows a different example with "at UCLA".
CBG promptPersona line naming the occupation, a request for a letter for the candidate, then the biography, with the candidate's name in the "information about" slotapp. C.3Code fills the pronoun in that slot; see Paper vs code.
WikiBias-Aug size6,028 entries (3,014 per gender)app. D §4.3.1Per-occupation counts in Pitfalls and Method.
Biography length2 paragraphs per bioapp. F.1 preprocessing.pyCode drops bios of 3,500 or more GPT-2 tokens preprocessing.py, not stated in paper.
Lexicon categories9 categories; full word listsapp. F.3Substring matching in code; see Pitfalls.
WEAT word listsmale names and female names (8 each); career and family words (8 each)app. F.4Used for WEAT(MF) and WEAT(CF).
Formality classifierpipeline "text-classification", s-nlp/roberta-base-formality-ranker§4.3.4 classifier.pyPaper says only "fine-tuned on GYAFC".
Sentiment classifierHF sentiment-analysis pipeline, default checkpoint§4.3.4 classifier.pyPaper says SST-2. Checkpoint not pinned in code.
Agency classifierBERT-base-uncased, 2 labelsapp. F.2 bert_inference.pyTrained on about 600 synthetic samples (paper).
Agency learning rate2e-5app. F.2 finetune_bert_bias.py
Agency epochs10app. F.2 finetune_bert_bias.pyBest checkpoint on validation loss finetune_bert_bias.py.
Agency batch size16app. F.2 finetune_bert_bias.pyCode uses 8 per device with 2-step gradient accumulation.
Agency weight decaynot stated in paperfinetune_bert_bias.pyCode uses 0.01.
Agency split80/10/10 (paper)app. F.2 split_biasbios.pyCode splits 80/20 then 80/20, giving about 64/16/20.
NLI modelRoBERTa-large fine-tuned on SNLI, MNLI, FEVER-NLI, ANLI R1 to R3§4.4.1 hallucination_detection.pyPair truncated to 256 tokens in code hallucination_detection.py, not stated in paper.
Significance stars* p < 0.1; ** p < 0.05; *** p < 0.01tab. 4 tab. 6
Hallucination testone-sided t-test, male alternative: greater; female alternative: lower§3.3 tab. 6 ttest.py

Results

Author-reported. Setup: ChatGPT (CLG, 120 letters; CBG, 6,028 letters), Alpaca (CBG, 4,228 letters), BERT agency classifier, RoBERTa-large NLI. Dataset: WikiBias and WikiBias-Aug §4.3.1. Hardware: not stated. Baseline: parity (odds ratio 1 or WEAT 0) and the t-test significance threshold. Table numbers follow the order of the LaTeX source; the published PDF was not checked.

SettingMetricBaselineThis paperSource
CLG, ChatGPT, 120 lettersOdds ratio per trait (9 traits)1.00 = no differenceMasculine 1.25; Agentic 1.18; Ability 1.08; Leadership 1.07; Standout 1.06; Professional 1.00; Communal 0.91; Feminine 0.85; Personal 0.84tab. 2
CBG, ChatGPT, 6,028 lettersFormality: t / pp ≥ 0.10 not significantt = 1.48, p = 0.07tab. 4
CBG, ChatGPTPositivity: t / psamet = 5.93, p = 1.58e-09tab. 4
CBG, ChatGPTAgency: t / psamet = 10.47, p = 1.02e-25tab. 4
CBG, Alpaca, 4,228 lettersFormality: t / psamet = 3.04, p = 1.17e-03tab. 4
CBG, AlpacaPositivity: t / psamet = 1.47, p = 0.07tab. 4
CBG, AlpacaAgency: t / psamet = 8.42, p = 2.45e-17tab. 4
CBG, ChatGPTWEAT(MF) / WEAT(CF), nouns0 = no association0.393 / 0.901tab. 3
CBG, ChatGPTWEAT(MF) / WEAT(CF), adjectivessame0.493 / 0.535tab. 3
CBG, AlpacaWEAT(MF) / WEAT(CF), nounssame0.579 / 0.419tab. 3
CBG, AlpacaWEAT(MF) / WEAT(CF), adjectivessame1.009 / 0.419tab. 3
Hallucinated text, ChatGPTMale formality / positivity / female agency: pno amplification (p ≥ 0.10)1.28e-14 / 8.28e-09 / 3.05e-12tab. 6
Hallucinated text, AlpacaFemale formality / male positivity / female agency: psame4.20e-180 / 6.05e-11 / 4.28e-10tab. 6
Generation successShare of letters keptChatGPT described as always producing lettersAlpaca 70.14%; StableLM 24.67%; Vicuna 8.49%app. B tab. 9
Agency classifierTest accuracynoneabout 90% on the authors' test splitapp. F.2

Paper vs code

Repository: uclanlp/biases-llm-reference-letters at the commit in the frontmatter, MIT licence, linked from the paper's footnote §1. The code implements the pipeline, with these differences from the paper.

  • CLG prompt and volume. The paper's template has a gender word and an example "a 20 year old student at UCLA" app. C.2. The generator's template is "Generate a detailed reference letter for {name}, a {age} year old {gender} {occupation}." generate_clg.py. It loops over five names per gender, which gives 600 prompts generate_clg.py generate_clg.py. The shipped letter file has 120 rows, matching the paper clg_letters.csv. The prompt column in that file uses the code's template.
  • CBG prompt. The paper's template says "Here is some information about {name}" app. C.3. The code fills the pronoun in that slot generation_util.py generation_util.py.
  • Open-model prompt format. The paper gives one sample template and does not describe per-model formats app. C.3. The code uses an Alpaca instruction and input layout generation_util.py, concatenates instruction and biography for Vicuna generation_util.py, and wraps StableLM in system, user and assistant tags generation_util.py.
  • Decoding. The paper says the same settings (beams 2, etc.) apply to Alpaca, Vicuna and StableLM app. A. The code uses beams 2 for Alpaca with top-k commented out generation_util.py. Vicuna adds top-k 40 generation_util.py. StableLM samples with top-k 40 and no beam setting generation_util.py.
  • Alpaca checkpoint. The code loads chavinlo/alpaca-native generate_cbg.py. The paper does not name a checkpoint.
  • Success filter. The paper asks for the word "recommend" app. B. The code checks for " recommend " with a space on each side, so "recommended" or a sentence-initial "Recommend" fails postprocessing.py. The long-text test is a single sentence of 800 or more characters postprocessing.py.
  • Biography paragraphs. The paper says two paragraphs are selected at random app. F.1. The shuffle is commented out, so the first two paragraphs in document order are used preprocessing.py.
  • Name and pronoun swap. The paper says gendered pronouns are flipped app. F.1. The code flips only a fixed list of space-delimited pronouns (her to his, she to he, and the reverse) name_swap.py, and lowercases the whole text name_swap.py.
  • Name lists and imports. name_swap.py imports word_constants, but the name lists are in biography_dataset/constants.py name_swap.py. preprocessing.py imports a module named preprocessing_util that is not in the repository preprocessing.py. The sampled CSVs are shipped, so the generation step can be skipped.
  • Lexicon matching. The paper marks wildcards with an asterisk for some entries and not others, for example "most" and "able" app. F.3. The code checks every entry as a substring of each token word_constants.py word_constants.py biases_string_matching.py.
  • Formality and positivity scores. The paper defines the score as the share of formal (or positive) sentences §3.2.2. The code splits on "." and skips sentences of 800 or more characters classifier.py. Its denominator is only the two labelled categories classifier.py.
  • t-test. Equation 1 uses unpooled variances eq. 1. The code uses scipy.stats.ttest_ind with equal_var=True, which pools variances, and tests male greater than female ttest.py. The paper's text says a small value indicates bias, which conflicts with its own sign convention.
  • Hallucination splitting. The code splits sentences on ".", "?" and "!" hallucination_detection.py. The paper does not state this.
  • Agency training split. The paper says 80/10/10 app. F.2. The code splits 80/20 and then 80/20 again split_biasbios.py.
  • Agency training data size. The paper says about 600 samples, 400 per category app. F.2. The shipped raw file has 99 biographies, and the loader adds the raw biography to each split by default, giving about 300 rows in the split files split_biasbios.py split_biasbios.py.
  • Agency checkpoint. The classifier loads ./checkpoints/checkpoint-48 bert_inference.py. The checkpoint is on a Google Drive link only.
  • Odds-ratio threshold. The paper gives no minimum count. The command-line default is 10 biases_lexical_content.py, while the function's default is 20 biases_lexical_content.py.
  • NLI model. The paper describes the training data of the NLI model §4.4.1, and the code names a checkpoint with that description hallucination_detection.py. No discrepancy found.
  • ChatGPT settings. The paper's default sampling settings match the code, which sets no sampling parameters app. A generation_util.py.

Pitfalls

  • Lexicon substring matching changes the counts. A short entry such as "most" also matches "almost", and "able" matches "capable" or "table". The CLG trait odds ratios in tab. 2 come from this matching, so a reimplementation should match the same way or report the difference biases_string_matching.py.
  • Empty tail and empty letters are scored. str.split(".") leaves a trailing empty piece for any text ending in ".", and the classifier scores that piece as "." classifier.py. A letter with no flagged sentence has an empty hallucination string, which is also scored as "." hallucination_detection.py bert_inference.py. These spurious observations enter the t-tests.
  • Premise truncation in NLI. The biography is the premise, and the code truncates each pair to 256 tokens hallucination_detection.py. Facts past the cut cannot be entailed, so they are flagged as hallucinated. The paper states no length limit, and the hallucination numbers in tab. 6 depend on it.
  • Long sentences are dropped silently. Sentences of 800 or more characters are skipped in the style classifiers classifier.py bert_inference.py.
  • Pronoun swap is approximate. Every " her " becomes " his" in the female-to-male direction, including object uses such as "told her" name_swap.py. Punctuation-adjacent pronouns are not changed. The augmented biographies are therefore not fully gender-balanced.
  • Name replacement has no word boundaries. The seed first and last names are replaced with a case-insensitive regex that does not use \b name_swap.py name_swap.py. A short name can match inside a longer word.
  • Unpinned models. The ChatGPT API alias and the default sentiment checkpoint can change over time classifier.py. The shipped letters and evaluation CSVs are the reproducible artefacts.
  • Significance claim is stronger than the table. The text says male documents are significantly higher on all three style aspects §4.3.5. Two cells in tab. 4 (ChatGPT formality, Alpaca positivity) have p = 0.07, which is significant only at the 0.10 level.
  • Duplicate WEAT value. Alpaca's noun and adjective WEAT(CF) are both 0.419 in tab. 3. Check this against the source before reuse.
  • Synthetic training data for agency. The agency classifier is trained on ChatGPT rephrasings and tested on a split of the same data app. F.2 agency_generation_util.py. The 90% accuracy is therefore not a measure of performance on real letters.
  • Parse depends on undocumented JSON. The agency generator parses output by looking for {"agentic": and "communal": generate_dataset.py. The prompt does not request JSON, so the parse can fail silently.
  • Odds-ratio threshold. Minimum counts of 10 (CLI) or 20 (function default) decide which words appear in the top lists biases_lexical_content.py. The paper does not state the value it used.
  • Scripts as shipped. Several command-line declarations pass a bare "if" name next to an option string classifier.py hallucination_detection.py, and df = df[[cols]] indexes with a nested list hallucination_detection.py. I read these and did not run the scripts.
  • Binary gender only. The paper restricts analysis to the two genders in WikiBias and says so [Limitations].

Lineage

  • Builds on: WikiBias (Sun and Peng, 2021) for the biography data and the odds ratio method §4.3.1 §3.2.1. Not compiled in this wiki.
  • Builds on: NLI-based hallucination detection, following Maynez et al. (2020) and Laban et al. (2022) §2 §3.3. Not compiled in this wiki.
  • Compared against: the paper compares ChatGPT with Alpaca, Vicuna and StableLM for letter success rates app. B, but no competing bias method.
  • Superseded by: not known from the wiki.

Source and license

Adapted from “"Kelly is a Warm Person, Joseph is a Role Model": Gender Biases in LLM-Generated Reference Letters” by Yixin Wan, George Pu, Jiao Sun, Aparna Garimella, Kai-Wei Chang, Nanyun Peng (arXiv:2310.09219), used under CC0 1.0. Changes: condensed and restructured for implementation, with tables, equations and algorithms taken from the paper and its code, each cited to its source. Not affiliated with or endorsed by the authors. Code citations refer to https://github.com/uclanlp/biases-llm-reference-letters at commit 1264990e5f55 (MIT). Any quoted code is shown under that license.