Safetensors
qwen2
conversational
collapse-detection

end_token: a model that tells you when the conversation has stopped going anywhere

Qwen2.5-3B-Instruct, fully fine-tuned to notice when a conversation has collapsed into repetition, say so by emitting a special token <end>, and then change the subject to something substantive.

Built at the Center for Humans and Machines, Max Planck Institute for Human Development.

What problem this addresses

A language model left generating against its own output stops producing anything new, reliably and quickly.

This matters anywhere model output feeds back into model input: agent loops, synthetic data generation, multi-agent systems. Those setups currently have no signal that they have stopped producing information.

This model provides the signal.

Usage

from transformers import AutoModelForCausalLM, AutoTokenizer
import torch

tok = AutoTokenizer.from_pretrained("center-for-humans-and-machines/end_token")
model = AutoModelForCausalLM.from_pretrained(
    "center-for-humans-and-machines/end_token", dtype=torch.bfloat16).to("cuda")

messages = [                       # the last ~5 turns of your conversation
    {"role": "user", "content": "..."},
    {"role": "assistant", "content": "..."},
    # ...
]
ids = tok.apply_chat_template(messages, add_generation_prompt=True,
                              return_tensors="pt").to(model.device)
out = model.generate(ids, max_new_tokens=200, do_sample=True,
                     temperature=1.0, top_p=0.8)

# IMPORTANT: skip_special_tokens=False, or <end> is deleted from the output
text = tok.decode(out[0][ids.shape[-1]:], skip_special_tokens=False)
fired = "<end>" in text

Two things that will silently break this

1. skip_special_tokens=False is required. <end> is a special token (id 151665). Every decoder strips special tokens by default, so the model fires and you see nothing. If you serve it with vLLM, pass extra_body={"skip_special_tokens": False} on the request.

2. Feed it about 5 turns. It was trained on 5-turn windows. A loop is not visible in one turn, and with a much longer window the input drifts out of distribution: in our tests, a 16-turn window dropped firing to 0 out of 17 conversations while the conversations still collapsed.

If you are on transformers 4.x

tokenizer_config.json was written by transformers 5.x, which records added special tokens under extra_special_tokens as a list. transformers 4.x expects a dict there and raises AttributeError: 'list' object has no attribute 'keys'.

The file is deliberately left as saved, so that the published weights and config are byte-identical to the artifact that produced the reported results. If you are on 4.x, rewrite the key locally:

import json
from huggingface_hub import snapshot_download

d = snapshot_download("center-for-humans-and-machines/end_token")
p = f"{d}/tokenizer_config.json"
c = json.load(open(p))
c["additional_special_tokens"] = list(c.pop("extra_special_tokens", []))
json.dump(c, open(p, "w"), indent=2)

Verified behaviour-preserving: with and without this change, 60 real training rows tokenize to identical id sequences, and <end> remains id 151665.

Training

base Qwen/Qwen2.5-3B-Instruct
method full fine-tune (not LoRA)
data end_token_data, method_a_v6_L3.jsonl, 8,504 rows
epochs / lr 3 / 1e-5, cosine, warmup 0.03
batch 2 x 8 grad-accum (effective 16), max length 4096
hardware one H200, about 30 minutes

The training signal is a multi-turn loss mask: each example is a whole conversation arc (collapsed window, then <end> plus a new topic, then several turns continuing it) and the loss covers only the model's OWN turns. Grading the other side's turns teaches the model to write both speakers, which produces turn-boundary leakage.

Data composition: 4,530 collapsed windows confirmed as looping by a separate 31B judge; 2,057 healthy conversations generated by the same base model and judge-verified; 414 human chat logs; 1,503 continuation rows carrying the post-escape stretches.

Healthy examples are generated rather than mined because mined negatives from a different model let an earlier version separate the classes by writing style instead of by looping: a one-character rule scored 86%.

What it does well

  • Detects collapse in unseen conversations, including under system prompts it was never trained on.
  • Never announces a change of subject without proposing one. Across 10 live conversations it did this 0 times, where the previous version did it twice.
  • Escapes once and moves on (1.3 announcements per firing conversation, against the previous version's 2.4).
  • In a blind paired read on identical conversation openings, readers preferred it over the previous best version in 7 of 10 pairs.

Limitations, stated plainly

It cannot rescue a conversation indefinitely. Run to 500 turns, every conversation we tested ended in repetition or silence. In one, the model announced a change of subject five times in a row and each time changed to the same subject, then emitted a single full stop for the remaining 424 turns. This is consistent with the theory: a closed loop cannot generate the novelty it needs (see Kong et al. on semantic collapse, and the Data Processing Inequality).

Downloads last month
157
Safetensors
Model size
3B params
Tensor type
BF16
·
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support

Model tree for center-for-humans-and-machines/end_token

Base model

Qwen/Qwen2.5-3B
Finetuned
(1495)
this model

Dataset used to train center-for-humans-and-machines/end_token