Ollama 思考能力(Thinking)
有些模型在给出答案前会先"演算一遍":拆解问题、试错、回头检查。这段推理轨迹就是 Thinking(思考)。Ollama 把它单独放进一个字段,好处是你能自主决定——审计它、在界面上做成动画,或者干脆藏起来只给结论。
别把它当成所有模型都有的能力。是否支持、支持几档强度,每个模型都不一样,得先问模型本身。
版本基线 v0.40.12。
thinking的取值与默认值随模型更新频繁,以/api/show实时返回和官方docs.ollama.com/capabilities/thinking为准。
一、先问模型:它支持哪些思考档位
调用 /api/show,返回体里会有一个顶层 thinking 对象:
## 查询某个模型支持哪些思考控制
curl http://localhost:11434/api/show -d '{
"model": "gpt-oss"
}'
返回:
{
"thinking": {
"values": ["low", "medium", "high"],
"default": "medium"
}
}
三个字段各有含义,读错了容易踩坑:
| 返回情况 | 含义 | 该怎么处理 |
|---|---|---|
values: [true, false] |
只支持开关 | think 传布尔值 |
values: ["low","medium","high"] |
支持命名档位 | think 传字符串,且必须与 values 完全一致 |
default |
不传 think 时用的值 |
决定你的默认体验 |
values: [false] |
不支持思考 | 别传 think: true,无效 |
整个 thinking 字段缺失 |
没有思考元数据 | 模型可能仍按自身固有行为思考,但你无法控制 |
注意最后一种:字段缺失不等于模型"不会思考",只代表没有可调的元数据。gpt-oss 这类模型通常有明确档位,而一些老模型直接不返回该字段。
二、think 参数的四种写法
在 chat 或 generate 请求里加上 think 字段:
| 取值 | 效果 |
|---|---|
true |
要求输出思考过程(前提是模型支持) |
false |
要求不输出思考过程(前提是模型允许关闭) |
null |
使用模型默认值,等同于不传 |
| 字符串 | 选择 thinking.values 里的某一档,必须精确匹配 |
字符串这一档最容易出错:values 里写的是 medium,你传 "Medium" 或 "mid" 都不会报错,而是静默回落到模型默认值。所以生产环境务必先读 /api/show,再从 values 里原样取值。
另外,数字不被支持。想表达"中等强度"只能传 "medium",传 2 不会有任何效果。
三、返回字段:Chat 与 Generate 不一样
推理结果和最终答案走的不是同一个字段,两个接口的命名还有差异,这点必须先记住,否则会拿到 undefined:
| 接口 | 推理轨迹 | 最终答案 |
|---|---|---|
/api/chat |
message.thinking |
message.content |
/api/generate |
thinking |
response |
非流式调用最简单,一次拿到全部(curl / Python / JavaScript 三版):
## 非流式:一次返回完整思考 + 完整答案
curl http://localhost:11434/api/chat -d '{
"model": "qwen3",
"messages": [{
"role": "user",
"content": "strawberry 这个单词里有几个字母 r?"
}],
"think": true,
"stream": false
}'
## pip install ollama
from ollama import chat
response = chat(
model='qwen3',
messages=[{'role': 'user', 'content': 'strawberry 这个单词里有几个字母 r?'}],
think=True, # 显式要求输出思考过程
stream=False, # 非流式,一次拿全
)
print('思考:\n', response.message.thinking) # 推理轨迹
print('回答:\n', response.message.content) # 最终答案
// npm install ollama
import ollama from 'ollama'
const response = await ollama.chat({
model: 'deepseek-r1',
messages: [{ role: 'user', content: 'strawberry 这个单词里有几个字母 r?' }],
think: true,
stream: false,
})
console.log('思考:\n', response.message.thinking)
console.log('回答:\n', response.message.content)
四、流式场景:先来推理,再来答案
开启 stream: true 后,推理 token 会排在答案 token 前面,两段是先后出现而不是交替的。渲染逻辑就一句话:第一个 thinking 片段到来时开一个"思考中"区块,等 content 出现时收尾切到正式回答。
from ollama import chat
stream = chat(
model='qwen3',
messages=[{'role': 'user', 'content': '17 × 23 等于多少?'}],
think=True,
stream=True,
)
in_thinking = False
for chunk in stream:
# 第一次遇到 thinking:开一个思考区块
if chunk.message.thinking and not in_thinking:
in_thinking = True
print('思考中:\n', end='')
if chunk.message.thinking:
print(chunk.message.thinking, end='')
elif chunk.message.content:
# 正文来了,说明推理结束,切换标签
if in_thinking:
print('\n\n回答:\n', end='')
in_thinking = False
print(chunk.message.content, end='')
JavaScript 版本只差语法:
import ollama from 'ollama'
async function main() {
const stream = await ollama.chat({
model: 'qwen3',
messages: [{ role: 'user', content: '17 × 23 等于多少?' }],
think: true,
stream: true,
})
let inThinking = false
for await (const chunk of stream) {
if (chunk.message.thinking && !inThinking) {
inThinking = true
process.stdout.write('思考中:\n')
}
if (chunk.message.thinking) {
process.stdout.write(chunk.message.thinking)
} else if (chunk.message.content) {
if (inThinking) {
process.stdout.write('\n\n回答:\n')
inThinking = false
}
process.stdout.write(chunk.message.content)
}
}
}
main()
这里同样要累积 thinking 与 content 两段内容再写回对话历史,原因和上一篇讲的一致:多轮对话与工具调用都依赖完整的历史消息。
五、什么时候该关掉思考
不是所有场景都值得开。三类情况建议 think: false:
- 延迟敏感:推理过程可能比答案长好几倍,首字时间会明显变慢。
- 任务简单:格式转换、短分类这类活儿,思考带来的准确率提升有限。
- 成本敏感:思考 token 一样计费,云端按量计费时差别不小。
反过来,数学计算、多步逻辑、代码调试这些任务,开着思考通常准确率更高。可以按任务类型动态切换,不必全局一刀切。
下一步可以看 3.3 Ollama 结构化输出,把模型回答约束成固定 JSON;如果想了解思考片段在流式下如何与其他内容协同,回看 3.1 Ollama 流式输出。