聊一聊Qwen3思考模式實現以及背后原理探討
硬開關
我們先通過官方的示例代碼來體驗一下,如何實現在思考模式和非思考模式之間切換
通過tokenizer.apply_chat_template的enable_thinking參數來實現
默認情況下,Qwen3 啟用了思考功能,類似于 QwQ-32B。這意味著模型將運用其推理能力來提升生成響應的質量。例如,當在tokenizer.apply_chat_template明確設置enable_thinking=True或保留其默認值時,模型將啟動其思考模式。代碼如下:
text = tokenizer.apply_chat_template(
messages,
tokenize=False,
add_generation_prompt=True,
enable_thinking=True # True is the default value for enable_thinking
)
在這種模式下,模型會生成包裹在...塊中的思考內容,然后是最終的響應。
對于思考模式,官方提示請使用Temperature=0.6、TopP=0.95、TopK=20和MinP=0( 中的默認設置generation_config.json)。請勿使用貪婪解碼,因為它會導致性能下降和無休止的重復。https://huggingface.co/Qwen/Qwen3-32B
Qwen3提供了一個硬開關,可以嚴格禁用模型的思考行為,使其功能與之前的 Qwen2.5-Instruct 模型保持一致。此模式在需要禁用思考以提高效率的場景中尤為有用。
text = tokenizer.apply_chat_template(
messages,
tokenize=False,
add_generation_prompt=True,
enable_thinking=False # Setting enable_thinking=False disables thinking mode
)
在此模式下,模型不會生成任何思考內容,也不會包含...塊。
對于非思考模式,我們建議使用Temperature=0.7、TopP=0.8、TopK=20和MinP=0。
軟開關
Qwen3示例給了一種高級用法: 通過用戶輸入在思考模式和非思考模式之間切換,具體來說就是通過用戶輸入層控制是否思考,提供了一種軟切換機制,允許用戶在enable_thinking=True時動態控制模型的行為。實現方式就是可以在用戶提示或系統消息中添加/think和/no_think,以便在不同回合之間切換模型的思維模式。
在多回合對話中,模型將遵循最后一條的指令。
以下是多輪對話的示例:
from transformers import AutoModelForCausalLM, AutoTokenizer
class QwenChatbot:
def __init__(self, model_name="Qwen/Qwen3-32B"):
self.tokenizer = AutoTokenizer.from_pretrained(model_name)
self.model = AutoModelForCausalLM.from_pretrained(model_name)
self.history = []
def generate_response(self, user_input):
messages = self.history + [{"role": "user", "content": user_input}]
text = self.tokenizer.apply_chat_template(
messages,
tokenize=False,
add_generation_prompt=True
)
inputs = self.tokenizer(text, return_tensors="pt")
response_ids = self.model.generate(**inputs, max_new_tokens=32768)[0][len(inputs.input_ids[0]):].tolist()
response = self.tokenizer.decode(response_ids, skip_special_tokens=True)
# Update history
self.history.append({"role": "user", "content": user_input})
self.history.append({"role": "assistant", "content": response})
return response
# Example Usage
if __name__ == "__main__":
chatbot = QwenChatbot()
# First input (without /think or /no_think tags, thinking mode is enabled by default)
user_input_1 = "How many r's in strawberries?"
print(f"User: {user_input_1}")
response_1 = chatbot.generate_response(user_input_1)
print(f"Bot: {response_1}")
print("----------------------")
# Second input with /no_think
user_input_2 = "Then, how many r's in blueberries? /no_think"
print(f"User: {user_input_2}")
response_2 = chatbot.generate_response(user_input_2)
print(f"Bot: {response_2}")
print("----------------------")
# Third input with /think
user_input_3 = "Really? /think"
print(f"User: {user_input_3}")
response_3 = chatbot.generate_response(user_input_3)
print(f"Bot: {response_3}")
思考模式軟硬開關如何兼容
我們會自然想到一種情況,就是用戶即在tokenizer.apply_chat_template中設置了enable_thinking參數,又在用戶輸入的時候可能加入了/think和/no_think符號,這種情況軟硬開關如何兼容呢?
為了實現 API 兼容性,當enable_thinking=True時,無論用戶使用/think還是/no_think,模型都會始終輸出一個包裹在<think>...</think>中的塊。
但是,如果禁用思考功能,此塊內的內容可能為空。當enable_thinking=False時,軟開關無效。無論用戶輸入任何/think或/no_think符號,模型都不會生成思考內容,也不會包含...塊。
我們可以看到enable_thinking的優先級是要大于/think或/no_think的優先級。
接下來我們通過Qwen3模型中的tokenizer_config.json來分析如何通過對話模板來如何實現快慢思考的?下面截圖就是:
圖片
多說一句,Chat template 是大語言模型(LLM)中的一個關鍵組件,它定義了如何將對話轉換為模型可以理解的格式化輸入。我將詳細解釋其原理并提供具體示例。
Qwen3的 Chat Template 原理詳解
Chat template 是大語言模型(LLM)中的一個關鍵組件,它定義了如何將對話轉換為模型可以理解的格式化輸入。我將詳細解釋其原理并提供具體示例。
基本原理
Chat template 本質上是一個文本模板,使用特定語法(通常是 Jinja2 模板語法)來處理和格式化對話數據。它的主要功能是:
- 角色區分:明確標識不同參與者(系統、用戶、助手等)的消息
- 特殊標記插入:添加模型訓練時使用的特殊標記
- 結構化表示:將多輪對話組織成模型預期的格式
- 功能支持:處理工具調用、思考過持等高級功能
關鍵組件解析
在 Qwen3-8B 的 chat_template 中:
- <|im_start|> 和 <|im_end|>:標記消息的開始和結束
- role:標識消息發送者(system、user、assistant、tool)
- <think></think>:圍繞助手的思考過程
- <tool_call></tool_call>:包含工具調用信息
- <tool_response></tool_response>:包含工具響應信息
實例說明
讓我通過幾個具體例子來說明 chat_template 如何工作:
例子1:簡單對話
假設有以下對話:
系統:你是一個有用的助手
用戶:你好
助手:你好!有什么我可以幫助你的?
使用 Qwen3-8B 的 chat_template 處理后,會變成:
<|im_start|>system
你是一個有用的助手
<|im_end|>
<|im_start|>user
你好
<|im_end|>
<|im_start|>assistant
你好!有什么我可以幫助你的?
<|im_end|>
例子2:包含思考過程的對話
系統:你是一個有用的助手
用戶:42+28等于多少?
助手:(思考:我需要計算42+28,42+28=70)
70
處理后:
<|im_start|>system
你是一個有用的助手
<|im_end|>
<|im_start|>user
42+28等于多少?
<|im_end|>
<|im_start|>assistant
<think>
我需要計算42+28,42+28=70
</think>
70
<|im_end|>
例子3:工具調用
系統:你是一個有用的助手
用戶:查詢北京今天的天氣
助手:我將調用天氣API
(調用天氣工具)
工具響應:北京今天晴朗,溫度22-28度
助手:根據查詢,北京今天天氣晴朗,溫度在22-28度之間。
處理后:
<|im_start|>system
你是一個有用的助手
<|im_end|>
<|im_start|>user
查詢北京今天的天氣
<|im_end|>
<|im_start|>assistant
我將調用天氣API
<tool_call>
{"name": "weather_api", "arguments": {"city": "北京", "date": "today"}}
</tool_call>
<|im_end|>
<|im_start|>user
<tool_response>
北京今天晴朗,溫度22-28度
</tool_response>
<|im_end|>
<|im_start|>assistant
根據查詢,北京今天天氣晴朗,溫度在22-28度之間。
<|im_end|>
Chat template 在實際處理中利用了 Jinja2 模板引擎的多種功能:
- 條件處理:{%- if ... %} 和 {%- endif %} 用于根據消息類型選擇不同的格式化方式
- 循環遍歷:{%- for message in messages %} 用于處理多輪對話
- 變量替換:{{- message.content }} 插入實際消息內容
- 命名空間:{%- set ns = namespace(...) %} 跟蹤狀態信息
Chat template 對模型使用至關重要:
- 訓練一致性:確保推理時的格式與訓練時一致
- 功能支持:使模型能識別并正確處理特殊功能(工具調用、思考過程)
- 性能優化:正確的格式化可以顯著提高模型輸出質量
下面一個代碼可以看到enable_thinking為False/True時不同的分詞結果
prompt = "42+28等于多少?"
messages = [
{"role": "user", "content": prompt}
]
text = tokenizer.apply_chat_template(
messages,
tokenize=False,
add_generation_prompt=True,
enable_thinking=True # Setting enable_thinking=False disables thinking mode
)
圖片
可以看到上面分詞之后的唯一差別的地方就是,enable_thinking=False時添加了一個<think>\n\n</think>\n,添加了一個空白思考,用來告訴模型已經思考結束。
那么好奇問了,如果enable_thinking=True,然后我在對話的后面添加一個<think>\n\n</think>\n會怎么樣呢,就是下面:
prompt = "42+28等于多少?"+“<think>\n\n</think>\n”
messages = [
{"role": "user", "content": prompt}
]
text = tokenizer.apply_chat_template(
messages,
tokenize=False,
add_generation_prompt=True,
enable_thinking=True # Setting enable_thinking=False disables thinking mode
)
這個時候其實還是會輸出思考內容的,大家可以試試看看
思考:Qwen3模型如何實現快慢思考
通過軟開關,我們可以看到Qwen3是可以通過從輸入上來控制的快慢思考的,自然而然想到的是不是在訓練的時候加了一些策略,直覺就是:
有一部分數據帶有思維鏈一部分沒有帶有思維鏈
- no_think:問題+答案
- think:問題+【思考內容+答案】
在第三階段,Qwen3在一份包括長思維鏈數據和常用的指令微調數據的組合數據上對模型進行微調,將非思考模式整合到思考模型中。確保了推理和快速響應能力的無縫結合。
這個地方可以做到軟硬的實現,一個是硬開關優先級的控制,可以通過強化學習來實現,比如模板部分是否存在<think>\n\n</think>\n,另一個就是軟開關,可能就是不同訓練數據混在一起訓練,讓模型面臨簡單或者復雜問題自主決定思考的自由度。
有同學說,針對Qwen3有些模型師Moe,是不是可以:
把解碼頭改成多專家的形式,一個think一個不think,這樣可行嗎
這個是可以的,不過Qwen3 8B不是Moe
也有同學說deepseek之前文章也是這么搞的,r1通過注入思考空指令如何不進行思考,
或者存在一種情況
類似于在R1應用中sys_prompt寫成:
think>用戶說我是一個端水大師,要從aaa bbb cc方面考慮問題,現在我要先做…… </think>
以期實現減少、甚至截斷模型原本reasoning的部分
一些控制思考深度的研究方法
筆者收集了一些資料,在深度學習與自然語言處理公眾號看到一些關于大模型“過度思考”解讀的一些文章
讓模型學會「偷懶」
論文:Towards Thinking-Optimal Scaling of Test-Time Compute for LLM Reasoning 鏈接:https://arxiv.org/pdf/2502.18080
解決方案:讓模型自己決定「想多久」論文提出Thinking-Optimal Scaling(TOPS)策略,核心思想是:學會「偷懶」。
三步走實現:
- 模仿學習:用少量樣例教模型「不同難度用不同思考長度」;
- 動態生成:對同一問題生成短/中/長三種思考鏈;
- 自我改進:自動選擇最短的正確答案作為訓練數據。
LightThinker的“思考壓縮”
論文:LightThinker: Thinking Step-by-Step Compression 鏈接:https://arxiv.org/pdf/2502.15589 項目:https://github.com/zjunlp/LightThinker
LightThinker的秘籍分為兩大招:何時壓縮和如何壓縮
何時壓縮?
LightThinker提供了兩種策略:
- Token級壓縮:每生成固定數量的token就壓縮一次,簡單粗暴但可能把一句話“腰斬”。
- 思考級壓縮:等模型寫完一個完整段落(比如一段分析或一次試錯)再壓縮,保留語義完整性,但需要模型學會“分段”。
如何壓縮?
這里有個關鍵操作:
- 把隱藏狀態壓縮成“要點token”!數據重構:訓練時在數據中插入特殊標記(比如觸發壓縮,[C]存儲壓縮內容),教模型學會“記重點”。
- 注意力掩碼:設計專屬注意力規則,讓壓縮后的token只能關注問題描述和之前的壓縮內容,避免“翻舊賬”。
在思考中提前預判答案
論文:Reasoning Models Know When They’re Right: Probing Hidden States for Self-Verification 鏈接:https://arxiv.org/pdf/2504.05419v1
通過訓練一個簡單的“探針”(類似體檢儀器),研究者發現:
- 高準確率:探針預測中間答案正確性的準確率超過80%(ROC-AUC >0.9)。
- 提前預判:模型甚至在答案完全生成前,隱藏狀態就已暴露正確性信號!
研究團隊設計了一套流程:
- 切分推理鏈:將長推理過程按關鍵詞(如“再檢查”“另一種方法”)分割成多個片段。
- 標注正確性:用另一個LLM(Gemini 2.0)自動判斷每個片段的答案是否正確。
- 訓練探針:用兩層神經網絡分析模型隱藏狀態,預測答案正確性。
總結
圖片
混合推理模型已經有不少了,例如 Claude 3.7 Sonnet 和 Gemini 2.5 Flash, Qwen3 應該是開源且效果好的典例。未來這可能也是一個趨勢,不需要特意區分普通模型和思考模型,而是同一個模型按需使用。
Claude 3.7 sonnet 的混合推理模型
Claude 3.7 sonnet 的混合推理模型(Hybrid Reasoning Model)是 LLM 和 reasoning model 的結合的新范式,之后大概率所有 AI labs 的模型發布模型都會以類似形式,社區也不會再單獨比較 base model 和 reasoning model 的能力。
使用 Claude 3.7 Sonnet 時,用戶可以通過“extended thinking” 的設置選擇是否需要輸出長 CoT:
? 打開 extended thinking,則輸出 CoT step-by-step 思考,類似開啟了人類的 Slow thinking 并且其思考長度是可以選擇的,因此 extended thinking 并不是 0 或 1,而是一個可以拖動的光譜,
? 關閉 extended thinking,則和 LLM 一樣直接輸出。
圖片
這個設計其實 Dario 很早就暗示過,在他看來:base model 與 reasoning model應該是個連續光譜。Claude System Card 中提到,extended thinking 的開關與長短是通過定義 system prompt 來實現的。我們推測要實現這樣的融合模型,應該需要在 RL 訓練之后通過 post training 讓模型學會什么時候應該 step by step thinking,如何控制推理長度。
對于這個新范式,我們的預測是:
- 之后的 hybrid reasoning model 需要在 fast thinking 和 slow thinking 的選擇上更加智能,模型自己具備 dynamic computing 能力,能規劃并分配解決一個問題的算力消耗和 token 思考量。Claude 3.7 Sonnet 目前還是將 inference time 的打開和長短交由用戶自己來決定,AI 還無法判斷 query 復雜度、無法根據用戶意圖自行選擇。
- 之后所有頭部 research lab 發布模型都會以類似形式,不再只是發 base model。
其實現在打開 ChatGPT 上方的模型選擇,會彈出五六個模型,其中有 4o 也有 o3,用戶需要自行選擇是用 LLM 還是 reasoning model,使用體驗非常混亂。因此,hybrid reasoning model 從智能能力和用戶體驗看都是下一步的必然選擇。
圖片
Gemini 2.5 Flash精細化的思維管理控制
我們知道,不同案例會以不同的方式權衡質量、成本和延遲。為了讓開發者具備靈活性,Gemini 2.5 Flash研發出了可供設置的思考預算功能,開發者可借此在模型進行思考的同時,對其可以生成的最大令牌數實現精細控制。預算更高意味著,模型可以更深入地進行推理,進而提高質量。值得注意的是,雖然預算使 2.5 Flash 的思考深度存在上限,但若提示無需深度思考,模型便不會耗盡全部預算。
圖片
低推理需求提示:
示例 1:“謝謝”用西班牙語怎么說?
示例 2:加拿大有多少個省份?
中等推理需求提示:
示例 1:投擲兩個骰子,點數之和為 7 的概率是多少?
示例 2:我所在的健身房會于周一、周三和周五的上午 9 點至下午 3 點,以及周二、周六的下午 2 點至晚上 8 點開放籃球活動。我每周工作 5 天,工作時間為上午 9 點至下午 6 點,我想在平日打 5 個小時籃球,請為我制定可行的時間表。
高推理需求提示:
示例 1:長度 L=3m 的懸臂梁有一個矩形截面(寬度 b=0.1m,高度 h=0.2m),且由鋼 (E=200 GPa) 制成。其整個長度承受 w=5 kN/m 的均勻分布載荷,自由端則承受 P=10 kN 的集中載荷。計算最大彎曲應力 (σ_max)。
示例 2:編寫函數 evaluate_cells(cells: Dict[str, str]) -> Dict[str, float],用以計算電子表格單元格值。
每個單元格包含:
一個數字(例如“3”) 或一個使用 +、-、*、/ 和其他單元格的公式,如“=A1 + B1 * 2”。 要求:
解析單元格之間的依賴關系。 遵守運算符優先級(*/ 優于 +-)。 檢測循環并指出 ValueError(“在<單元格>檢測到循環”)。 無 eval()。僅使用內置庫。