Back to Sentence Transformers

Prompts and Instructions

skills/train-sentence-transformers/references/prompts_and_instructions.md

5.5.06.4 KB
Original Source

Prompts and Instructions

Modern embedding models (E5, BGE, GTE, Qwen3-Embedding, Nomic, Instructor, etc.) use prompts / instructions at encode time — short prefixes like "query: ", "passage: ", or "Represent this sentence for retrieval:" — to signal task intent.

In sentence-transformers, prompts work during both training and inference, and the library keeps them aligned automatically.

The prompt is literally prepended to the input text before tokenization.

Model-level prompts

Set prompts on the model so encode(), encode_query(), encode_document() use them automatically:

python
model = SentenceTransformer("your-base-model")
model.prompts = {
    "query":    "query: ",
    "document": "passage: ",
}
model.default_prompt_name = "document"        # used when none is specified

At inference:

python
q_emb = model.encode_query("What is the capital of France?")
# Internally: encodes "query: What is the capital of France?"

d_emb = model.encode_document(["Paris is the capital of France.", "Berlin is the capital of Germany."])
# Internally: encodes "passage: Paris..." etc.

# Explicit prompt_name:
emb = model.encode(["some text"], prompt_name="query")

Training with prompts

Set prompts on the training args so they're applied automatically to the right columns during training. Four shapes, increasingly specific:

1. Single prompt (applied everywhere)

python
args = SentenceTransformerTrainingArguments(
    ...,
    prompts="Represent this sentence for similarity: ",
)

Every input column gets the prefix. Simplest and often sufficient for single-task training.

2. Per-column prompts

python
prompts = {
    "anchor":   "query: ",
    "positive": "passage: ",
    "negative": "passage: ",
}

Keys are column names. Different prefixes for different roles.

3. Per-dataset prompts (multi-dataset)

python
prompts = {
    "all-nli": "Classify the entailment relationship: ",
    "stsb":    "Score semantic similarity: ",
}

Keys are dataset names (matching the train_dataset dict keys).

4. Per-dataset + per-column

python
prompts = {
    "all-nli": "",                                          # all-nli: no prompt
    "msmarco": {                                            # msmarco: per-column
        "query":    "query: ",
        "positive": "passage: ",
        "negative": "passage: ",
    },
}

Cross-encoder specifics

Cross-encoders restrict prompts to single-value or per-dataset only (no per-column). This is because cross-encoders take text pairs, not separate columns.

python
args = CrossEncoderTrainingArguments(
    ...,
    prompts="Rank this passage for the query: ",   # single prompt
)
# or
args = CrossEncoderTrainingArguments(
    ...,
    prompts={"msmarco": "...", "gooaq": "..."},    # per-dataset
)

Sparse encoders (SPLADE)

Sparse encoders support prompts with the same four shapes as bi-encoders (added in v5.x). Same model.prompts = {...} API.

Pooling and prompt tokens (bi-encoder)

When using mean/last-token pooling with prompt prefixes, the prompt tokens themselves can either:

  • Be included in the pooled embedding (default behavior). Simpler, what most models do.
  • Be excluded — only the actual content tokens are pooled.

To exclude, flip include_prompt on the Pooling module — found via isinstance rather than a fixed index, since multimodal / Router pipelines don't always put Pooling at position 1:

python
from sentence_transformers.sentence_transformer.modules import Pooling

for module in model:
    if isinstance(module, Pooling):
        module.include_prompt = False

Or use the helper if your model exposes one (e.g. SparseEncoder.set_pooling_include_prompt(False)).

When to exclude: when the prompt is purely a task signal and you don't want it to dilute the semantic representation. Most E5 / BGE / GTE models leave prompts included.

During training with prompts=, the include_prompt setting is respected; the trainer also tracks include_prompt_lengths internally so pooling skips the prompt tokens correctly when include_prompt=False.

Inference alignment

If you trained with prompts={"query": "query: ", ...}, you must:

  1. Save the prompts on the model: model.prompts = args.prompts before save_pretrained, OR let the library do this automatically via the model card data.
  2. Use the same prompts at inference: call encode_query() / encode_document() and the saved prompts are applied.

If the saved model's config_sentence_transformers.json has prompts + default_prompt_name, anyone who loads it gets the correct prompts for free.

Instruction tuning (different from prompts)

Some models (Instructor, Qwen3-Embedding) use instructions — longer descriptions like "Represent the biomedical query for retrieving relevant passages:". In sentence-transformers these are modeled the same way: just set them as prompts.

The model-card convention is to list available instructions/prompts in a dict:

python
model.prompts = {
    "msmarco-query":    "Represent the query for MS MARCO retrieval: ",
    "msmarco-doc":      "Represent the passage for MS MARCO retrieval: ",
    "sts":              "Represent the sentence for semantic similarity: ",
    "classification":   "Represent the sentence for topic classification: ",
}

Users call model.encode(["..."], prompt_name="msmarco-query").

Gotchas

  • Training with prompts but forgetting to set them on the model before save_pretrained: the saved model won't know about the prompts. Users will encode without the prefix and get bad results. Fix: model.prompts = args.prompts before saving, or use SentenceTransformerModelCardData(...) with the prompt info.
  • Using encode_query() at inference but didn't train with a "query" prompt: the method will just call regular encode (no prefix applied), which is fine. But the documentation implies you use it, and users may be confused.
  • Trailing spaces matter: "query: " vs "query:" — the former has a trailing space before the real text. Inspect your training data to know which one your model was trained with.
  • Mixing prompted and non-prompted data in multi-dataset training is fine as long as your prompts dict covers it per-dataset (e.g. {"dataset_a": "query: ", "dataset_b": ""}).
  • include_prompt=False with small datasets: the model may under-fit because you've effectively shortened every input. Usually leave as True.