跳到主要内容

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. 常见错误与对策

  1. Schema 过于复杂:嵌套超过 5 层会导致模型遵循率下降。对策:尽量打平结构。
  2. 缺乏校验:即使有结构化输出,也可能出现数值越界。对策:在解析后增加 Bean Validation (JSR 303)。
  3. 忽略解析失败:网络波动或模型偶尔的失误。对策:实现带有明确错误反馈的重试机制。

6. 结构化输出决策树

需要结构化输出?


┌───────────────────────────────────────┐
│ 是否需要 100% 严格遵守 Schema? │
└───────────────────────────────────────┘

┌───┴───┐
│ │
是 否
│ │
▼ ▼
┌─────────┐ ┌─────────────────────────────┐
│ OpenAI? │ │ 是否仅需轻量化结构? │
└─────────┘ └─────────────────────────────┘
│ │
┌───┴───┐ ┌───┴───┐
│ │ │ │
是 否 是 否
│ │ │ │
▼ ▼ ▼ ▼
使用 使用 使用 使用标准
OpenAI 工具 XML JSON 模式
专有 调用 标签 并配合
接口 (任意) (任意) 校验/重试

总结

结构化输出是 Agentic 系统 的生命线。

  1. JSON 模式 是不同系统间集成的标准。
  2. XML 标签 是复杂输入组织的最优解。
  3. Spring AI 提供了 Java 开发者所需的类型安全与工程化封装。

上一章2.2 核心推理模式下一章2.4 Spring AI 提示词实战 → stone