1. Ollama思考模式变更的背景与影响
最近在使用Spring AI集成Ollama时,我发现一个重要的API变更:Ollama的思考内容不再包含在常规的content字段中,而是独立到了thinking字段。这个变化直接影响到了我们获取模型推理过程的方式。
在Ollama 0.12之前的版本中,模型的推理思考过程通常与最终答案一起混在content字段中。这种设计导致开发者需要手动解析内容,区分哪些是中间思考过程,哪些是最终答案。随着Ollama对推理模型(如Qwen3、DeepSeek系列)支持的增强,新版本将thinking内容完全独立出来,形成了更清晰的数据结构。
这个变更带来的主要影响包括:
- 思考内容与最终答案完全分离,不再需要复杂的文本解析
- 可以通过metadata直接访问完整的推理链条
- 支持更精细的思考级别控制(如GPT-OSS模型的高/中/低思考级别)
- 为后续的推理过程分析和优化提供了结构化数据基础
2. Spring AI中获取思考内容的新方法
2.1 基础获取方式
在Spring AI中,现在可以通过ChatResponse的metadata来获取思考内容。以下是基本代码示例:
java复制ChatResponse response = chatModel.call(
new Prompt(
"计算17乘以23的值并展示计算过程",
OllamaChatOptions.builder()
.model("deepseek-r1")
.enableThinking() // 明确启用思考模式
.build()
));
// 获取思考过程
String thinking = response.getResult().getMetadata().get("thinking");
System.out.println("模型思考过程: " + thinking);
// 获取最终答案
String answer = response.getResult().getOutput().getText();
System.out.println("最终答案: " + answer);
对于支持思考级别的模型(如GPT-OSS),可以指定不同的思考深度:
java复制// 低思考级别 - 适合简单问题
OllamaChatOptions.builder().thinkLow()
// 中思考级别 - 适合中等复杂度问题
OllamaChatOptions.builder().thinkMedium()
// 高思考级别 - 适合复杂推理问题
OllamaChatOptions.builder().thinkHigh()
2.2 流式响应中的思考内容处理
当使用流式API时,思考内容会作为独立的chunk发送。处理方式如下:
java复制Flux<ChatResponse> stream = chatModel.stream(
new Prompt(
"解释量子纠缠现象",
OllamaChatOptions.builder()
.model("qwen3")
.enableThinking()
.build()
));
stream.subscribe(response -> {
String thinking = response.getResult().getMetadata().get("thinking");
String content = response.getResult().getOutput().getText();
if (thinking != null && !thinking.isEmpty()) {
System.out.println("[思考过程] " + thinking);
}
if (content != null && !content.isEmpty()) {
System.out.println("[响应内容] " + content);
}
});
2.3 通过OpenAI兼容接口获取
如果使用Spring AI的OpenAI客户端连接Ollama,思考内容会出现在"reasoningContent"字段中:
java复制@Configuration
class OllamaConfig {
@Bean
OpenAiChatModel ollamaChatModel() {
return OpenAiChatModel.builder()
.options(OpenAiChatOptions.builder()
.baseUrl("http://localhost:11434/v1")
.apiKey("ollama")
.model("deepseek-r1")
.build())
.build();
}
}
// 使用示例
ChatResponse response = chatModel.call(
new Prompt("'strawberry'单词中有多少个字母'r'?"));
String reasoning = response.getResult().getMetadata().get("reasoningContent");
System.out.println("推理过程: " + reasoning);
3. 思考模式的实际应用场景
3.1 数学问题求解
思考模式特别适合展示数学问题的分步解决过程。例如:
java复制Prompt mathPrompt = new Prompt(
"解方程: 3x + 5 = 17",
OllamaChatOptions.builder()
.model("deepseek-r1")
.enableThinking()
.build());
ChatResponse mathResponse = chatModel.call(mathPrompt);
System.out.println("解题步骤:");
System.out.println(mathResponse.getResult().getMetadata().get("thinking"));
System.out.println("最终答案:");
System.out.println(mathResponse.getResult().getOutput().getText());
输出可能类似于:
code复制解题步骤:
1. 首先从等式两边减去5: 3x = 12
2. 然后两边除以3: x = 4
最终答案:
方程的解是x=4
3.2 编程问题调试
对于编程问题,思考模式可以展示模型的推理链条:
java复制Prompt codePrompt = new Prompt(
"找出下面Python代码中的错误:\n"
+ "def calculate_average(numbers):\n"
+ " sum = 0\n"
+ " for number in numbers:\n"
+ " sum += number\n"
+ " return sum",
OllamaChatOptions.builder()
.model("qwen3")
.enableThinking()
.build());
ChatResponse codeResponse = chatModel.call(codePrompt);
3.3 复杂决策分析
对于需要多步推理的决策问题,思考模式能提供更透明的决策过程:
java复制Prompt decisionPrompt = new Prompt(
"我应该选择React还是Vue作为新项目的前端框架?"
+ "项目需求是:需要快速开发,团队有JavaScript经验但无特定框架经验,"
+ "需要良好的生态系统支持。",
OllamaChatOptions.builder()
.model("gpt-oss")
.thinkHigh() // 使用高级思考模式
.build());
4. 常见问题与解决方案
4.1 思考内容为空的情况
如果获取到的thinking字段为空,可能的原因和解决方法包括:
-
模型不支持思考模式:
- 确认使用的模型是否支持思考功能(如qwen3:*-thinking、deepseek系列)
- 解决方案:更换为支持思考的模型版本
-
思考模式未启用:
- 检查是否调用了.enableThinking()或设置了相应选项
- 解决方案:明确启用思考模式
-
问题过于简单:
- 对于简单问题,模型可能不生成中间思考
- 解决方案:尝试更复杂的问题
4.2 思考内容与最终答案的协调
有时需要将思考过程与最终答案结合展示。推荐的做法是:
java复制public String formatResponse(ChatResponse response) {
StringBuilder sb = new StringBuilder();
String thinking = response.getResult().getMetadata().get("thinking");
if (thinking != null && !thinking.isEmpty()) {
sb.append("### 思考过程\n").append(thinking).append("\n\n");
}
sb.append("### 最终答案\n")
.append(response.getResult().getOutput().getText());
return sb.toString();
}
4.3 性能考量
启用思考模式会增加模型的响应时间和计算资源消耗。优化建议:
- 对于简单查询,禁用思考模式
- 使用适当的思考级别(如GPT-OSS的thinkLow/thinkMedium/thinkHigh)
- 考虑缓存常见问题的思考结果
java复制// 根据问题复杂度动态启用思考
OllamaChatOptions options = OllamaChatOptions.builder()
.model("qwen3")
.thinkIf(question -> question.length() > 50) // 自定义条件
.build();
5. 高级应用与最佳实践
5.1 结构化思考输出
结合Ollama的结构化输出功能,可以获取更规范的思考过程:
java复制String jsonSchema = """
{
"type": "object",
"properties": {
"reasoning_steps": {
"type": "array",
"items": {
"type": "object",
"properties": {
"step": {"type": "string"},
"explanation": {"type": "string"}
}
}
},
"final_answer": {"type": "string"}
}
}
""";
Prompt structuredPrompt = new Prompt(
"解释光合作用的过程",
OllamaChatOptions.builder()
.model("deepseek-v3.1")
.enableThinking()
.outputSchema(jsonSchema)
.build());
5.2 思考内容的分析与可视化
可以将思考内容解析为更易读的格式:
java复制public void analyzeThinking(String thinking) {
// 简单按行分割思考过程
String[] steps = thinking.split("\n");
System.out.println("共识别出 " + steps.length + " 个推理步骤:");
for (int i = 0; i < steps.length; i++) {
System.out.println((i+1) + ". " + steps[i].trim());
}
}
5.3 与RAG流程的集成
在检索增强生成(RAG)场景中,思考模式可以帮助理解模型的检索决策:
java复制@Bean
public RetrievalAugmentor retrievalAugmentor() {
return new RetrievalAugmentor(
new ContentRetriever(),
new OllamaChatModel(
OllamaApi.builder().build(),
OllamaChatOptions.builder()
.model("deepseek-r1")
.enableThinking()
.build()
)
);
}
// 在RAG响应中获取思考内容
ChatResponse ragResponse = retrievalAugmentor.augment(prompt);
String retrievalThinking = ragResponse.getResult().getMetadata().get("thinking");
5.4 模型微调与思考质量
如果需要更高质量的思考输出,可以考虑:
- 使用更大的模型版本
- 提供更详细的问题描述
- 在系统消息中明确思考要求
java复制Message systemMessage = Message.builder(Role.SYSTEM)
.content("你是一个科学导师。请详细展示你的思考过程,分步骤解释你的答案。")
.build();
Prompt detailedPrompt = new Prompt(
List.of(systemMessage, userMessage),
OllamaChatOptions.builder()
.model("qwen3:70b-thinking")
.enableThinking()
.build());
