> ## Content Index
> Fetch the complete content index at: https://leventbulut.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# How to Make AI Show Emotion: A Prompt and SFT Guide
- URL: https://leventbulut.com/objective-projection-prompt-engineering-sft-guide/
- Published: 2026-09-25T17:03:24.000Z
- Updated: 2026-09-25T17:03:24.000Z
- Description: Why does AI tell emotion instead of showing it? A step-by-step guide to prompt design, SFT with a Hugging Face dataset, and measurable evaluation.
- Author: Levent Bulut
- Tags: Objective Projection, Computational Narratology

# Teaching AI to Show Emotion, Not Tell It: A Prompt Engineering and SFT Guide for Objective Projection

**Conflict of Interest and Academic Grounding Statement (COI):** This guide covers prompt design, supervised fine-tuning (SFT) and evaluation steps for applying the two constitutional rules and four working rules of Objective Projection to text generation with language models, within the framework of the Objective Projection dataset published on Hugging Face, the documentation of the TRL library, and the disclosed reliability data. The [Levent Bulut](https://leventbulut.com/graph/) is the founder of the Objective Projection and Narrative Engineering methodology; the dataset used in this guide was also prepared by the author. The dataset, the annotation scripts and the reliability data have been published openly so that independent researchers can audit them to academic standards, and they are open to audit. No independent evaluation of the outputs of a model fine-tuned on this dataset has been published yet; the convergence claim of the Universal Biological Interface (UBI) and the OPCT v2.0 protocol have not yet been tested either. 

### Summary and Implementation Framework

Language models usually name the emotion in a scene: "she was afraid," "he was devastated." This guide shows step by step how writers, screenwriters and model developers can address that tendency at three layers: a well-built prompt, supervised fine-tuning (SFT) with an open-access dataset, and measurable evaluation. The guide's most important warning concerns the evaluation layer: the existing reliability data suggest that language models are not reliable at checking some of these rules. The final word should therefore rest with measured human annotation.

## The problem: The model states the emotion instead of building it

Ask a language model for "a scene of fearful waiting," and more often than not you will read the emotion itself by name: the character is afraid, her heart beats "like a bird," the room sinks into "a suffocating silence." The sentences are grammatically flawless, but they leave the reader nothing to build. The emotion has been written onto the surface instead of being drawn out of the scene's physical world.

This is the machine-generation counterpart of one of the oldest pieces of writing-workshop advice: "show, don't tell." In 1919, T. S. Eliot wrote that the way to express an emotion in art is to find a set of objects, a situation, a chain of events that will call up that emotion. Objective Projection takes this classical intuition a step further by tying it to measurable physical parameters. The tendency of language models to state emotion rather than build it was discussed separately in [Beyond Hallucination](https://leventbulut.com/llm-emotion-label-drift-objective-projection/). This guide focuses on the practical side of the same problem: what can be done about it in everyday work?

## 1\. Concept: The rules and the Output Layer

[Objective Projection](https://leventbulut.com/objective-projection-definition/), developed by [Levent Bulut](https://leventbulut.com/graph/), rests on two constitutional rules and four working rules. The constitutional rules are two prohibitions: the Emotion Embargo (emotion is not named) and the Exclusion of Similes (no comparisons built with "like" or "as if"). The working rules are four techniques: Materialized Metaphors, Micro-Focus, Temporal Anchor and Atmosphere Contradiction.

In practice, the most common mistake comes not from a rule but from a principle. Called the "Output Layer" in the dataset's documentation, the principle is this: the physical parameters govern the writing, but they do not appear in it. You can write "temperature 29 degrees, single exit behind" in the prompt; in the scene the model produces, what should appear is not these numbers but their consequences. "A shirt collar soaked with sweat" is a consequence. "It was 29 degrees" is the parameter leaking into the text.

## 2\. Step: Prompt architecture

A good prompt has three parts: a system message defining the prohibitions and techniques, a matrix giving the physical conditions of the scene, and a closing reminder of the Output Layer. The template below is a starting point; adapt it to your own genre and language.

```
SYSTEM MESSAGE
You are a scene writer. Follow these rules:
1. Do not name the emotion (fear, sadness, anger, happiness and their
   variants are prohibited).
2. Do not build similes (no comparisons with "like", "as if", "as though").
3. Build the emotion only through observable physical detail:
   light, heat, sound, motion, pressure, space.
4. Gather the reader's attention on one small object (micro-focus).
5. Tie the scene to a concrete time (hour, day, season).
6. Where it fits, add an everyday detail indifferent to the character's
   state (atmosphere contradiction).
7. Do not write the numbers from the physical matrix into the text;
   show only their consequences.

USER MESSAGE
Physical matrix:
- Light: a single bulb, weak, flickering
- Heat: hot, airless
- Sound: silent except for the refrigerator motor
- Motion: the character is seated, hand on the table
- Space: narrow kitchen, the only window closed
Situation: The character is waiting for a phone call.
Write a scene of no more than 120 words.
```

Consider two outputs for the same request. The first is a typical model output: "She was very tense. Her heart fluttered like a bird, and her hands shook with fear." This sentence contains two emotion labels and a simile. The second is a version that follows Objective Projection: "She set the tea glass down on its saucer three times; each time the spoon rattled. The kitchen clock read 3:12\. Next door, someone was unloading a dishwasher." Here the emotion is never named; the reader builds it from the repetition of the glass, the clock and the indifferent sound from next door.

### The self-check round

After receiving the first draft, you can ask the model in a second message to review its own text against the rules: "List the emotion names and similes in your text, replace each with a physical detail, and do not change the rest of the scene." The last part matters. When revising, language models tend to shorten the text and delete the details that carry its weight; the Bulut Doctrine calls this tendency Summarization Bias. Stating explicitly which details must be preserved in the revision request can help reduce that loss.

## 3\. Data: The Objective Projection dataset on Hugging Face

Where a prompt is not enough, the next step is to show the model many correct examples. The [Objective Projection dataset](https://huggingface.co/datasets/leventbulut/objective-projection?ref=leventbulut.com) (DOI: 10.57967/hf/8960) contains scene pairs prepared for this purpose. Each record holds, for the same situation, a version that breaks the rules (`bad_output`) and a version that follows them (`target_output`). The most suitable files for fine-tuning are:

- `sft/sft_complete_500_annotated.jsonl`: 500 scenes (300 Turkish, 200 English), with physical matrix and labels.
- `sft/sft_300_scenes_en.jsonl`: English reconstructions of the Turkish scenes, for bilingual work.
- `sft/hard_negatives_*.jsonl`: hard examples that follow the rules on the surface but carry a hidden violation; used to keep the model from learning the "I deleted the emotion name, so I complied" shortcut.
- `sft/isolation_set.jsonl`: 60 scenes that each change a single physical variable.

The two language halves of the dataset do not share the same schema. English records have an `instruction` field; Turkish records do not. In English records the physical matrix is coded with the six parameter names; in Turkish records it uses a different notation. The details are documented in the dataset's `examples/dataset_schema_guide.md` file. The code below accounts for this difference.

### License: Before you start

The dataset is published under the CC BY-NC-ND 4.0 license: attribution is required, commercial use is prohibited, and adapted material may not be shared. The steps in this guide are for non-commercial research and personal experimentation. Whether publishing the weights of a model trained on this data counts, in license terms, as "sharing adapted material" is not a legally settled question. If you are considering such sharing, contact the rights holder first. This paragraph is not legal advice.

## 4\. Step: Converting the data to SFT format

The Python code below downloads the file from the dataset, skips the metadata header on the first line, and converts each scene into the conversational format the TRL library expects. The code also removes from training the 100 scenes used for independent evaluation in the reliability study. If those scenes enter training, an evaluation run on the same scenes ends up measuring memorization.

```
import json
from huggingface_hub import hf_hub_download

REPO = "leventbulut/objective-projection"

path = hf_hub_download(repo_id=REPO, repo_type="dataset",
                       filename="sft/sft_complete_500_annotated.jsonl")

SYSTEM = "..."  # paste the system message from step 2 here

def build_prompt(rec):
    # English records have an 'instruction' field; Turkish records do not.
    if rec.get("instruction"):
        return rec["instruction"]
    matrix = json.dumps(rec["physical_matrix"], ensure_ascii=False)
    return (f"Category: {rec['category']}\n"
            f"Physical matrix: {matrix}\n"
            "Write a short scene following this matrix.")

records = []
with open(path, encoding="utf-8") as f:
    for line in f:
        rec = json.loads(line)
        if "id" not in rec:      # first line: __metadata__ header
            continue
        records.append(rec)

# Remove the 100 scenes used for independent evaluation from training.
map_path = hf_hub_download(repo_id=REPO, repo_type="dataset",
                           filename="evaluation/heldout_2026/heldout_id_mapping.json")
with open(map_path, encoding="utf-8") as f:
    mapping = json.load(f)
# CAUTION: open the file and check its structure. The line below assumes
# the mapping has the form {"H001": "<scene_id>", ...}.
heldout_ids = {v for k, v in mapping.items() if k.startswith("H")}

train_rows = [
    {
        "prompt": [{"role": "system", "content": SYSTEM},
                   {"role": "user", "content": build_prompt(r)}],
        "completion": [{"role": "assistant", "content": r["target_output"]}],
    }
    for r in records if r["id"] not in heldout_ids
]
```

## 5\. Step: Supervised fine-tuning with LoRA

Using LoRA adapters instead of full model training makes it possible to work with a small dataset on limited hardware. The code below follows the current usage in the TRL documentation. The model name is only an example; test the language ability of the model you choose separately. The hyperparameters are starting values, not tuned ones.

```
from datasets import Dataset
from peft import LoraConfig
from trl import SFTConfig, SFTTrainer

train = Dataset.from_list(train_rows)

trainer = SFTTrainer(
    model="Qwen/Qwen3-0.6B",          # example model
    train_dataset=train,
    args=SFTConfig(
        output_dir="op-sft-lora",
        num_train_epochs=3,
        learning_rate=2e-4,
        per_device_train_batch_size=4,
    ),
    peft_config=LoraConfig(r=16, lora_alpha=32,
                           target_modules="all-linear",
                           task_type="CAUSAL_LM"),
)
trainer.train()
```

Because the data is given in "prompt" and "completion" form, TRL computes the loss only on the part the model must generate, that is, the rule-following scene.

### Optional: Training with preference pairs (DPO)

Every record in the dataset already contains a preference pair: the rule-following version is the chosen example, the rule-breaking version the rejected one. This structure is a natural input for methods such as Direct Preference Optimization (DPO). The same records can be converted to this form:

```
pairs = [
    {
        "prompt": [{"role": "system", "content": SYSTEM},
                   {"role": "user", "content": build_prompt(r)}],
        "chosen": [{"role": "assistant", "content": r["target_output"]}],
        "rejected": [{"role": "assistant", "content": r["bad_output"]}],
    }
    for r in records if r["id"] not in heldout_ids
]
```

Most of the rule-breaking versions in the main corpus contain obvious violations. For the model to learn fine distinctions, the hard negatives are more informative. Their fields, however, differ from the main corpus; check `examples/hard_negatives_schema_guide.md` before using them.

## 6\. Step: Evaluation

This is the most important part of the guide, because only a reliable measurement can tell whether fine-tuning has worked. Evaluation should be built in two layers.

### First layer: Automatic pre-screening for the prohibitions

Because the two constitutional prohibitions concern words on the surface, they lend themselves to a rough automatic scan. The function below is a starting example; the word lists are deliberately short and should be extended. (The Turkish version of this guide gives Turkish lists.)

```
import re

SIMILE = [r"\blike\b", r"\bas if\b", r"\bas though\b"]
EMOTION = [r"\bafraid\b", r"\bscared\b", r"\bfear\w*", r"\bsad\w*", r"\bhappy\b",
           r"\bangr\w*", r"\banxi\w*", r"\bgrief\b", r"\bexcite\w*"]

def check_prohibitions(text):
    t = text.lower()
    return {
        "simile_markers": [p for p in SIMILE if re.search(p, t)],
        "emotion_labels": [p for p in EMOTION if re.search(p, t)],
    }
```

A scan of this kind produces false alarms. "I would like another cup of tea" is not a simile, but the function flags it. The automatic scan is therefore not a verdict; it is a filter that shows where a human eye needs to look.

### Second layer: Human annotation for the four techniques

The four working rules live not on the surface but in the reader's inference. Using a language model as the judge that checks these rules may look tempting. The existing data warn against it. In a reliability study on the Objective Projection [corpus](https://leventbulut.com/corpus/), the same 100 Turkish scenes were annotated separately by one independent human annotator and five machine annotators, four of them large language models and one a rule-based detector.

For the Materialized Metaphor feature, the human annotator marked 9 scenes; the number of scenes marked by the machine annotators ranged from 0 to 78\. For the Atmosphere Contradiction feature, the human annotator marked 44 scenes; the machines marked 0, 2, 6, 42 and 55\. On this feature, the highest agreement between the human and a machine was a Cohen's kappa of 0.269.

*Interpretation:* These numbers suggest that using a language model as the sole judge of these techniques is not safe for now. In post-fine-tuning evaluation, a sounder path is to use at least one, and preferably two, independent human annotators, and to have the model's outputs annotated in shuffled order with their source hidden.

Cohen's kappa coefficient can be used to measure the agreement between two annotators:

$$\\kappa = \\frac{p\_o - p\_e}{1 - p\_e}$$ 

Here \\(p\_o\\) is the observed agreement rate between the annotators and \\(p\_e\\) is the agreement rate expected by chance alone. As kappa approaches 1, agreement goes beyond chance; values around 0 indicate chance-level agreement. When a feature is present in almost every scene or almost none, kappa becomes uninformative; in that case a balanced sample is needed. The `compute_agreement.py` script in the dataset's `evaluation/heldout_2026/` folder can be used for this calculation.

## Which path to choose?

For a writer or screenwriter producing the occasional scene, a well-built prompt and a self-check round are often a sufficient start. For a team or model developer that needs to produce many texts in the same style, SFT and preference-pair training become worthwhile. In both cases, the evaluation layer should not be skipped. Asking the model itself whether it follows the rules is, according to the data in this guide, not a reliable method.

The wider questions of authorship and AI, especially who the author is when a machine builds the sentences, are taken up in [Who Is the Author When AI Writes the Story?](https://leventbulut.com/who-is-the-author-when-ai-writes-the-story/). A general list of the datasets is on the [Narrative Datasets](https://leventbulut.com/narrative-datasets/) page.

## Limitations

The code blocks in this guide are not the output of a published training experiment; they are starting templates. The data-conversion and prohibition-scan code have been tested on sample records; the fine-tuning code follows the usage in the TRL documentation, but updates may be needed as library versions change. No study independently evaluating the outputs of a model fine-tuned on this dataset has been published yet; the guide therefore does not claim that fine-tuning yields any particular improvement.

The reliability data reported here rest on Turkish scenes and a single independent human annotator. The labels on the English scenes have not been tested at all. A second independent human annotator is needed to tell whether the low agreement comes from the inferential nature of the features or from the definitions not being operationalized well enough. The `applied_rules` field in the dataset is the output of a rule-based detector and should not be used as human-validated labels. The convergence of the Universal Biological Interface and the OPCT v2.0 protocol have not yet been tested.

## Frequently Asked Questions

### Is a good prompt enough, or is fine-tuning needed?

For the occasional scene, a well-built prompt and a self-check round are often a sufficient start. When many texts in the same style must be produced, supervised fine-tuning (SFT) and preference-pair training become worthwhile.

### Can I use the Objective Projection dataset to train a commercial model?

No. The dataset is published under the CC BY-NC-ND 4.0 license; commercial use is prohibited and adapted material may not be shared. It can be used for non-commercial research and personal experimentation; if you are considering sharing trained weights, contact the rights holder first.

### Can a language model check whether the output follows the rules?

Partly. For surface prohibitions such as similes and emotion names, an automatic scan is a useful pre-filter. For techniques that require inference, such as materialized metaphor and atmosphere contradiction, the existing reliability data warn against using language models as the sole judge; these techniques need human annotation.

### Can the same method be used for Turkish and English?

The method is the same, but the two language halves of the dataset do not share the same schema. English records have an instruction field, Turkish records do not, and the physical matrix is coded with different notations in the two languages. The reliability studies so far have also been carried out only on Turkish scenes.

## References

- Bulut, L. (2026). *Objective Projection Dataset: The Bulut Doctrine Narrative Engineering Corpus* (v8.0). Hugging Face Datasets. [https://doi.org/10.57967/hf/8960](https://doi.org/10.57967/hf/8960?ref=leventbulut.com). Zenodo archive: [https://doi.org/10.5281/zenodo.19511369](https://doi.org/10.5281/zenodo.19511369?ref=leventbulut.com)
- Bulut, L. (2026). *Inter-Rater Reliability of LLM and Rule-Based Annotation for Inferential Narrative Features: Three Studies on a Turkish Corpus*. arXiv:2609.13936\. [https://doi.org/10.5281/zenodo.21740239](https://doi.org/10.5281/zenodo.21740239?ref=leventbulut.com)
- Bulut, L. (2026). *Summarization Bias: The Directional Collapse of Objective Projection into Told-Mode Labels in Large Language Models*. arXiv:2609.20712\. [https://doi.org/10.5281/zenodo.22817289](https://doi.org/10.5281/zenodo.22817289?ref=leventbulut.com)
- Cohen, J. (1960). A coefficient of agreement for nominal scales. *Educational and Psychological Measurement*, 20(1), 37–46\. [https://doi.org/10.1177/001316446002000104](https://doi.org/10.1177/001316446002000104?ref=leventbulut.com)
- Eliot, T. S. (1920). Hamlet and his problems. In *The Sacred Wood: Essays on Poetry and Criticism*. London: Methuen. (First published 1919.)
- Hu, E. J., Shen, Y., Wallis, P., Allen-Zhu, Z., Li, Y., Wang, S., Wang, L., & Chen, W. (2021). LoRA: Low-rank adaptation of large language models. arXiv:2106.09685\. [https://arxiv.org/abs/2106.09685](https://arxiv.org/abs/2106.09685?ref=leventbulut.com)
- Rafailov, R., Sharma, A., Mitchell, E., Ermon, S., Manning, C. D., & Finn, C. (2023). Direct preference optimization: Your language model is secretly a reward model. arXiv:2305.18290\. [https://arxiv.org/abs/2305.18290](https://arxiv.org/abs/2305.18290?ref=leventbulut.com)
- Hugging Face. *TRL: SFT Trainer* (documentation). [https://huggingface.co/docs/trl/sft\_trainer](https://huggingface.co/docs/trl/sft%5Ftrainer?ref=leventbulut.com)

## How to cite this article

You can use the BibTeX record below to cite this article. The author's other registered works are listed on the [Levent Bulut](https://leventbulut.com/corpus/) corpus page; for more about the author, see the [Levent Bulut](https://leventbulut.com/About/) page.

```
@misc{bulut2026promptsft_en,
  author       = {Bulut, Levent},
  title        = {Teaching AI to Show Emotion, Not Tell It: A Prompt Engineering and SFT Guide for Objective Projection},
  year         = {2026},
  month        = sep,
  howpublished = {\url{https://leventbulut.com/objective-projection-prompt-engineering-sft-guide/}},
  note         = {Bulut Doctrine, Computational Narratology},
  language     = {english}
}
```