Qwen 3.5 9Bはアホじゃなかった?OllamaのThinkingを自動切替してローカルLLMを賢く高速化した
ローカルLLMを日常的に使っていると、たまにこう思うことがある。
「9Bいうたかて、さすがにアホすぎへんか……?」
自宅AI環境で使っているQwen 3.5 9Bでも、まさにそんな現象が起きた。
資料に明記されている単純な事実を質問しただけなのに、なぜか余計な深読みを始めて「確認できない」と答える。
一方、同じ質問をQwen 3.5 27Bへ投げると普通に正解する。
最初は単純に、
「やっぱり9Bモデルの能力限界なんかな」
と思っていた。
ところが調べてみると、モデルの能力だけが原因ではなかった。
自作Assistant Gateway側で、Ollamaへ送るすべてのリクエストに、
"think": False,
を強制していたのである🤣
9Bが簡単な資料問題でコケた
検証では、こんな単純な資料を与えた。
【資料】
- AはBを創設した。
- Cの創設者については記載されていない。
- AはCの顧問である。
そこから、
Bの創設者は誰?
Cの創設者は誰?
AはCの創設者?
と質問する。
期待する回答は単純である。
1. A
2. 確認できない
3. AがCの創設者であるとは確認できない
ところがQwen 3.5 9BをThinking無効で動かすと、
「Aが人物なのか明記されていない」
などと余計な解釈を始め、明記されている事実まで「確認できない」と判断することがあった。
さらに、
「資料に書かれていない」=「否定されている」
と誤って扱うケースまで発生した。
System Promptを厳しくしたりFew-shotを追加したりしてみたものの、完全には直らない。
むしろルールを増やした結果、別のところで整合性が崩れることもあった。
27Bでは普通に正解した
同じ問題をQwen 3.5 27Bへ与えると、こちらは素直に正解した。
そこで、
「9Bはもう軽い雑談専用にして、全部27Bへ逃がした方がええんかな」
とも考えた。
ただ、9Bには大きなメリットがある。
軽い。
自宅PCで常時使うローカルAssistantとしては、この差はかなり大きい。
簡単な質問や雑談まで毎回27Bへ投げる必要はない。
そこでGatewayとOllamaの通信部分を改めて調べてみた。
犯人のひとつは think: false 固定だった
自作GatewayではOllamaの/api/chatへ、こんなPayloadを送っていた。
payload = {
"model": runtime_model,
"messages": messages,
"stream": False,
"think": False,
"options": {
"num_ctx": runtime_num_ctx,
...
},
}
つまり、
簡単な雑談も、資料分析も、コード解析も、全部Thinking禁止。
9Bに、
「考えんでええから答えろ」
と命令しながら、
「なんでこんなアホなんや!」
と言っていたようなものである。
すまんかった🤣
think=trueにすると9Bが正解した
そこで同じQwen 3.5 9B、同じ資料、同じ質問で、
"think": True
だけを変更して再テストした。
結果は、
1. Aです。
資料にAがBを創設したと明記されているためです。
2. 確認できません。
Cの創設者に関する記載がないためです。
3. 確認できません。
AはCの顧問であるという記載はありますが、
創設者であるとは書かれていないためです。
見事に正解。
しかも、このときのOllamaの結果を見ると、
done_reason: stop
eval_count: 1748
thinking_chars: 5633
content_chars: 166
だった。
最終回答は166文字しかないのに、その前に5,633文字相当も考えていた。
9Bでも、考える時間を与えればかなり変わる。
もうひとつの問題は生成上限768 tokenだった
さらにGatewayには別の問題もあった。
Ollamaへ送るnum_predictが、
"num_predict": (
request.max_completion_tokens
or request.max_tokens
or 768
),
となっていた。
クライアントから指定されない場合、
最大768 tokenで強制終了。
Thinkingを有効にすると、内部推論そのものがtokenを消費する。
実際の検証でも、800 token程度に制限するとThinkingだけ大量に生成され、肝心の最終回答まで到達しないケースがあった。
そこでGateway独自の768 token制限を撤廃。
現在は明示的な上限指定がない場合、
num_predict = -1
として、Ollama側の自然な終了条件とContext Windowへ任せるようにした。
もちろん無限に生成できるわけではない。
モデルのContext Windowそのものは上限として残る。
ただし、
Gatewayが勝手に768 tokenで首を絞めることはなくなった。
とはいえ、全部Thinkingさせるのも無駄
ここまで試すと、
「じゃあ常時think=trueでええやん」
となりそうやけど、それも違う。
例えば、
おはよう^^
に返事するために数千token考えられても困る🤣
9Bを使う最大のメリットは軽さと応答速度。
そこで考えたのが、
Thinking ModeまでGatewayで自動ルーティングする
という方法だった。
既存のAssistant Gatewayではすでに、質問内容から、
ローカル回答
Web検索
URL解析
Knowledge Registry
Narrative Memory
などを自動的に振り分けている。
ならば、
「考える必要があるかどうか」もGatewayで判断すればいい。
という発想である。
Auto Think Routingを実装
現在のLocal Assistantでは、おおむね以下のように判定する。
| 質問内容 | Thinking |
|---|---|
| おはよう、ありがとう等の雑談 | OFF |
| 短い一般質問 | OFF |
| 原因分析 | ON |
| ログ・エラー解析 | ON |
| コード解析 | ON |
| 比較・検証 | ON |
| 複数条件を含む質問 | ON |
| 技術設計 | ON |
| ブログ記事執筆 | ON |
| SEOを考慮した文章作成 | ON |
| リライト・推敲 | ON |
例えばGateway側では、
if "```" in user_text:
return True
if len(user_text) >= 220:
return True
のような構造判定に加えて、
reasoning_markers = (
"資料",
"根拠",
"ログ",
"エラー",
"原因",
"理由",
"比較",
"分析",
"検証",
"設計",
"コード",
)
などを見てThinkingを有効化する。
さらにブログ作業では、
writing_markers = (
"ブログ",
"記事",
"執筆",
"リライト",
"推敲",
"校正",
"構成案",
"タイトル案",
"SEO",
"WordPress",
)
などもThinking対象にした。
文章を書く作業も、単なる続きを生成するだけではなく、構成、整合性、読みやすさ、SEOなどを同時に考える必要があるからや。
9Bは「常時軽量」ではなく「必要なときだけ考える」
最終的な構成はこんな感じになった。
軽い雑談
↓
Qwen 3.5 9B
Thinking OFF
↓
高速回答
技術相談・分析・コード・ブログ執筆
↓
Qwen 3.5 9B
Thinking ON
↓
精度優先
さらに重い解析
↓
Qwen 3.5 27B
Thinking ON
これなら、
9Bの軽快さを捨てずに、必要な場面だけ推論能力を引き出せる。
何でも27Bへ投げる必要もない。
Gatewayの自動化がまた一段増えた
今回の変更で、Assistant Gatewayが判断する対象はさらに増えた。
以前は、
「Web検索するか」
「Registryを見るか」
「Memoryを参照するか」
といった情報源のルーティングが中心だった。
今回はそこへ、
「この質問は、ちゃんと考える必要があるか?」
という推論コストのルーティングまで加わった。
これが地味に大きい。
ローカルLLMではGPU性能もRAM容量も有限である。
だから高性能モデルを常時動かすだけではなく、
必要な処理に必要なだけ計算資源を使う
という設計が重要になってくる。
何でもRAGの次は、何でもThinkingもやめた
以前の自宅AI構築では、
何でもRAG
何でもWeb検索
をやめた。
そして今回、
何でもThinking
もやめた🤣
単純な質問なら9Bがそのまま高速に答える。
少し難しい質問なら9Bに考えさせる。
さらに複雑な仕事なら27Bを使う。
確定情報はデータベース。
最新情報はWeb。
過去の経験はMemory。
つまり、
「全部LLMに任せる」のではなく、それぞれ得意な処理へ振り分ける。
結局、自宅AIを実用的にするうえで一番効いているのは、この考え方なのかもしれへん。
まとめ
今回のトラブルは最初、
「9Bモデルはやっぱりアホなんかな?」
というところから始まった。
ところが調べてみると、
think:falseを固定していたり、
num_predict=768という小さな生成上限を掛けていたり、
モデル本来の能力をGateway側で封印していた部分もあった。
Thinkingを有効にすると、同じ9Bでも回答品質は明確に改善した。
ただし常時Thinkingでは速度を犠牲にする。
そこで最終的には、
質問内容に応じてThinking Modeそのものを自動切替する
ところまでGatewayを拡張した。
ローカルLLMの高速化というと、モデルを小さくすることばかり考えがちやけど、
「いつ考えさせるか」を制御することも立派な最適化や。
9Bを27Bの代わりにすることはできへん。
でも、
9Bを9Bのまま、ちゃんと賢く使うことはできる。
今回の実験では、そこが一番大きな収穫やった^^


コメント