4 结构化输出工程
为什么结构化输出很重要
在企业级应用中,强制执行结构化输出能将可靠性提升 3-5 倍。如果没有它,LLM 的输出将是不可预测的字符串,需要复杂的正则解析、错误 处理和重试逻辑。
可靠性难题
无结构化输出:
┌─────────────────────────────────────────────────────────────┐
│ LLM 响应: "这是分析结果: │
│ - 营收:可能在 5000 万美元左右 │
│ - 增长:强劲!约 15% │
│ - 风险:中等(见下方备注) │
│ 备注:数字均为估算..." │
└──────────────────────────────────── ─────────────────────────┘
↓ 解析地狱
- 正则提取在边缘案例下失效
- 数字格式不统一 ("$50M" vs "50000000")
- 风险等级不一致 ("Medium" vs "3/5")
- 额外的解释性文本导致 JSON 解析崩溃
有结构化输出:
┌─────────────────────────────────────────────────────────────┐
│ { │
│ "revenue": 50000000, │
│ "growthRate": 0.15, │
│ "riskLevel": "MEDIUM", │
│ "confidence": 0.85 │
│ } │
└─────────────────────────────────────────────────────────────┘
↓ 直接解析
- JSON.parse() 每次都能成功
- 类型安全的序列化与反序列化
- 自动根据 Schema 进行校验
- 消除歧义
1. JSON 模式:各供应商实现
1.1 OpenAI 结构化输出 (2024)
OpenAI 的“结构化输出 (Structured Outputs)”功能通过受约束的解码 (Constrained Decoding) 保证了 100% 的 Schema 符合率。模型在数学层面上被约束,只能生成符合你定义的 JSON Schema 的 Token。
关键创新
与仅保证 JSON 语法正确的“JSON 模式”不同,结构化输出确保生成的 JSON 完全匹配你定义的 Schema。这是通过在 Token 生成级别限制概率分布实现的。
实现示例 (TypeScript):
import { z } from 'zod';
import { zodResponseFormat } from 'openai/helpers/zod';
// 使用 Zod 定义 Schema
const AnalysisResult = z.object({
sentiment: z.enum(['positive', 'negative', 'neutral']),
confidence: z.number().min(0).max(1),
summary: z.string().max(500)
});
const response = await client.beta.chat.completions.parse({
model: 'gpt-4o-2024-08-06',
messages: [{ role: 'user', content: feedbackText }],
response_format: zodResponseFormat(AnalysisResult, 'analysis')
});
// 完全类型化的响应 —— 无需手动解析!
const analysis = response.choices[0].message.parsed;
1.2 Anthropic:工具调用方案
Anthropic 的 Claude 没有原生的 JSON 模式,但通过工具调用 (Tool Use / Function Calling) 实现了极佳的结构化输出效果。
Anthropic 的思路
Anthropic 建议将工具定义作为“输出 Schema”。模型通过调用带有结构化参数的工具,间接地生成了完美的 JSON 输出。
2. XML 标签:无 Schema 的结构化
XML 标签提供了一种轻量级的结构化方式,无需 API 级别的 Schema 强制。这种方法适用于 任何 LLM,且在 Claude 上表现尤为出色。
2.1 为什么 XML 优于 Markdown 分隔符
Markdown 分隔符 (容易混淆):
┌─────────────────────────────────────────────────────────────┐
│ ## 指令 │
│ 评审这段代码 │
│ │
│ ## 上下文 │
│ 电商平台 │
│ │
│ 问题:# 和 ## 经常出现在代码注释或正文中,模型易混淆边界 │
└─────────────────────────────────────────────────────────────┘
XML 标签 (极其可靠):
┌─────────────────────────────────────────────────────────────┐
│ <instructions> │
│ 评审这段代码 │
│ </instructions> │
│ │
│ <context> │
│ 电商平台 │
│ </context> │
│ │
│ 优势:边界无歧义、支持嵌套、便于程序化提取 │
└─────────────────────────────────────────────────────────────┘
3. Anthropic Prefilling (前缀预填充)
这是 Anthropic 的独家绝技,允许你预先填充助手的第一个回复片段,从而强行规定输出格式。
# 强制模型直接输出 JSON 数组,跳过废话
messages=[
{"role": "user", "content": "提取实体:Tim Cook 在加州宣布了 iPhone 16。"},
{"role": "assistant", "content": '{"entities": ['} # 预填充
]
# 模型将接着输出: '"Tim Cook", "California", "iPhone 16"]}'
4. Spring AI:生产级结构化输出
Spring AI 通过 OutputConverter(尤其是 BeanOutputConverter)提供了类型安全的结构化输出支持。
4.1 核心实现流
// 1. 定义你的 Java Record
public record FeedbackAnalysis(
@JsonPropertyDescription("整体情感") Sentiment sentiment,
@JsonPropertyDescription("置信度 0-1") Double confidence,
List<String> actionItems
) {}
// 2. Service 层集成
@Service
public class AnalysisService {
private final ChatClient chatClient;
public FeedbackAnalysis analyze(String text) {
return chatClient.prompt()
.user(u -> u.text("{text}\n\n{format}")
.param("text", text)
.param("format", new BeanOutputConverter<>(FeedbackAnalysis.class).getFormat()))
.call()
.entity(FeedbackAnalysis.class); // 自动反序列化为 Java 对象
}
}
5. 常见错误与对策
- Schema 过于复杂:嵌套超过 5 层会导致模型遵循率下降。对策:尽量打平结构。
- 缺乏校验:即使有结构化输出,也可能出现数值越界。对策:在解析后增加 Bean Validation (JSR 303)。
- 忽略解析失败:网络波动或模型偶尔的失误。对策:实现带有明确错误反馈的重试机制。
6. 结构化输出决策树
需要结构化输出?
│
▼
┌───────────────────────────────────────┐
│ 是否需要 100% 严格遵守 Schema? │
└───────────────────────────────────────┘
│
┌───┴───┐
│ │
是 否
│ │
▼ ▼
┌─────────┐ ┌─────────────────────────────┐
│ OpenAI? │ │ 是否仅需轻量化结构? │
└─────────┘ └─────────────────────────────┘
│ │
┌───┴───┐ ┌───┴───┐
│ │ │ │
是 否 是 否
│ │ │ │
▼ ▼ ▼ ▼
使用 使用 使用 使用标准
OpenAI 工具 XML JSON 模式
专有 调用 标签 并配合
接口 (任意) (任意) 校验/重试
总结
结构化输出是 Agentic 系统 的生命线。
- JSON 模式 是不同系统间集成的标准。
- XML 标签 是复杂输入组织的最优解。
- Spring AI 提供了 Java 开发者所需的类型安全与工程化封装。
上一章:2.2 核心推理模式 ← 下一章:2.4 Spring AI 提示词实战 → stone