主题
30 · 推理与思考(Thinking)
动机(Motivation)
有时除了拿到结构化结果,你还需要校验 LLM 的推理过程。
设想这样一个场景:用户想规划一次度假,指定偏好目的地是希腊和意大利,可用出行日期在 6 月、8 月或 9 月。 他们让 LLM 找出"一周行程、票价可承受"的航班选项。LLM 返回了一个结构化的 Flight 对象, 包含去程/返程日期、目的地与价格。即使输出符合预期的 schema,你可能仍然想确认:
- 航班日期确实落在所要求的月份内;
- 目的地确实在希腊或意大利,而不是别的地方。
如果航班详情超出了用户的条件范围,能访问 LLM 的推理过程就能帮你理解它为什么做了这些选择。
一个更重要的用例是:LLM 无法完成请求时 —— 例如因为用户条件含糊或自相矛盾而无法创建所要求的对象。 这时,即便没有产出结果,thinking 块也能解释哪里出了问题。
概念(Concepts)
ThinkingBlock—— 承载 LLM 推理细节的抽象,包括标签类型、标签值与推理文本内容。ThinkingTagType—— 定义推理标记类型的枚举:TAG:XML 风格标签;PREFIX:行前缀,例如//THINKING:;NO_PREFIX:JSON 输出之前未打标签的推理文本。
ThinkingResponse—— 响应包装器,同时持有结果对象与ThinkingBlock列表。ThinkingException—— 对象实例化失败时仍会保留 thinking 块的异常,使错误场景下也能调试。thinking()——PromptRunner上启用 thinking 提取的核心 API 方法。
示例:处理对象与 thinking 块
java
// 用 LLM 与可选工具配置 PromptRunner
PromptRunner runner = ai.withDefaultLlm() // 该示例使用 claude-sonnet-4-5
.withToolObject(Tooling.class);
String prompt = """
What is the hottest month in Florida and its average high temperature?
Please provide a detailed analysis of your reasoning.
""";
// 用 thinking() 启用 thinking 块提取
ThinkingResponse<MonthItem> response = runner
.thinking()
.createObject(prompt, MonthItem.class);
// 取结构化结果
MonthItem result = response.getResult();
// 取 LLM 的推理过程
List<ThinkingBlock> thinkingBlocks = response.getThinkingBlocks();
// 查看单个 thinking 块
for (ThinkingBlock block : thinkingBlocks) {
System.out.println("Type: " + block.getTagType()); // TAG、PREFIX 或 NO_PREFIX
System.out.println("Tag: " + block.getTagValue()); // 例如 "think"、"analysis"
System.out.println("Content: " + block.getContent());
}示例:优雅地处理失败
当 LLM 可能无法产出合法结果时,用 createObjectIfPossible:
java
ThinkingResponse<MonthItem> response = runner
.thinking()
.createObjectIfPossible(prompt, MonthItem.class);
MonthItem result = response.getResult();
if (result != null) {
// 处理结果
} else {
// 对象创建失败 —— 检查推理过程,理解原因
for (ThinkingBlock block : response.getThinkingBlocks()) {
logger.info("LLM reasoning: {}", block.getContent());
}
}提供方说明(Provider Notes)
Embabel 通过提供方中立的 API 暴露 thinking:
PromptRunner.thinking()LlmOptions.thinking
这仍然是启用推理/思考模式的主要公开 API。
在底层,各提供方集成可能对 Thinking 做不同翻译,以匹配提供方自身的能力。 例如,Google GenAI 会把 Embabel 的 thinking 选项映射到对应的 Spring AI Google GenAI chat options, 例如 includeThoughts 与 thinkingBudget。
调用方不需要任何新的应用层 thinking API。 总体上,既有应用应继续使用 Embabel 的通用 thinking API, 而不是提供方专属配置。
有些提供方还会在模型的 YAML 里定义模型级默认值,但显式的运行时 thinking 请求仍然走 LlmOptions.thinking。
提供方的行为可能略有差异,取决于 Spring AI 如何暴露推理数据:
- 部分提供方在 assistant message 本身上暴露推理;
- 另一些通过 generation metadata 暴露。
因此,提取出的 thinking 块是否存在、以及形态如何,可能随提供方与 Spring AI 集成版本而略有不同。