はじめに
Qwen3.5-9B に要約を頼んで、2,014字が返ってきた。全部が英語の "Thinking Process:" で、日本語の本文は無かった。
2026-09-04、Mac のローカル LLM に mlx-community/Qwen3.5-9B-MLX-4bit を足した。要約やふるい分けのような軽い処理を任せるためだった。
MLX(Apple Silicon 向けの機械学習フレームワーク)で動かし、既存の3種類の処理に通した。返ってきたのはこれだった。
| 処理 | 出力の長さ | 中身 |
|---|---|---|
| summarize | 2,014字 | すべて英語の "Thinking Process:" 以下 |
| decisions | 2,849字 | 同上 |
| filter | 520字 | 同上 |
3つとも、本文が1文字も無い。 出てきたのはモデルが考えている途中の英文だけで、答えに到達する前に max_tokens を使い切っていた。
27B のときは別のキーで止まっていた
同じ系統のモデルは、先に mlx-community/Qwen3.8-27B-4bit を入れていた。こちらでも思考が止まらない問題は起きていて、既定の思考量が xhigh のため、2,500トークン使っても思考の終わり(</think>)に届かない設問があった。
27B は reasoning_effort: low を渡すことで収まった。
2つのモデルは config.json の model_type がどちらも qwen3_5。同じ型なら同じキーで止まりそうに見える。
9B に reasoning_effort は効かなかった。
チャットテンプレートを見ると違いがそのまま出る
思考のオン・オフを受け取るのは、モデルに同梱されている chat_template.jinja だ。2つを grep した。
grep -c reasoning_effort chat_template.jinja
grep -c enable_thinking chat_template.jinja
| モデル | reasoning_effort |
enable_thinking |
|---|---|---|
| Qwen3.8-27B-4bit | 5 | 2 |
| Qwen3.5-9B-MLX-4bit | 0 | 1 |
9B のテンプレートには reasoning_effort という語が存在しない。存在しないキーを渡しても、テンプレートは黙って無視する。エラーにはならない。
9B で思考を止めるキーは enable_thinking だけだった。
開始タグが出力に現れない
もうひとつ、後処理が効かなかった理由がある。
このテンプレートは、add_generation_prompt のときに <think> をプロンプトの側に書き込む。モデルは「思考の途中」から生成を始めるので、出力はこういう形になる。
Thinking Process: ... </think>
本文
開始タグの <think> が無い。よくある除去の正規表現は素通りする。
re.sub(r"<think>.*?</think>", "", text, flags=re.S) # 開始タグが無いので一致しない
「先頭から </think> まで」を消すフォールバックを足せば、27B のように思考が終わるケースは救える。
text = re.sub(r"^.*?</think>", "", text, flags=re.S)
ただ今回の 9B は </think> にすら届いていない。消す対象の終わりが無いので、後処理ではどうにもならない。止める方法はキーの指定だけだった。
直したのは設定1行
呼び出し側はテンプレートに任意の引数を渡せるようにしてあったので、コードは変えていない。モデルごとの設定に1行ずつ足した。
# Qwen3.5-9B
model_id: mlx-community/Qwen3.5-9B-MLX-4bit
enable_thinking: false
# Qwen3.8-27B
model_id: mlx-community/Qwen3.8-27B-4bit
reasoning_effort: low
mlx-lm で直接呼ぶなら、apply_chat_template に同じ名前で渡す。
prompt = tokenizer.apply_chat_template(
messages,
add_generation_prompt=True,
tokenize=False,
enable_thinking=False, # 9B の場合。27B なら reasoning_effort="low"
)
enable_thinking: false を入れた設定で分類の処理に「今日は暑いので冷たい飲み物が欲しい」を通すと、返ってきたのは 食事 の1語だった。思考の英文は出ていない。
新しいモデルを足すときは、先に chat_template.jinja を reasoning_effort|enable_thinking で grep してからキーを決めるようにした。model_type が同じでも、テンプレートは別物として扱う。
同じ qwen3_5 だから、同じ止め方でいいと思っていた。
型の名前が揃っていても、止めるスイッチの名前はテンプレートの中にしか書いていなかった。