JSON 格式的优势
在应用开发过程中,JSON 是最常用的数据结构格式,具有显著优势:
- 轻量级:格式紧凑,比 XML 等其他数据交换格式更轻便,传输速度快,占用带宽少。
- 易于解析:基于文本,在各种编程语言中流行,可轻松解析和生成。
- 平台无关性:语言无关,可在不同系统和编程语言之间无缝交换数据。
- 支持复杂数据结构:表示复杂的对象和数组结构,适合层次化数据。
- 易于人类阅读:键值对格式,直观易懂。
- 自描述性:有意义的键名,有助于理解数据结构的目的。
因此,在 AI 应用开发中,希望大模型返回 JSON 结构数据,便于与现有系统集成及结果解析。
技术方案
1. 纯提示词方案
早期大模型不支持 JSON 结构返回,目前部分模型仍不支持。前期只能通过 Prompt(提示词)实现。
Prompt 示例:查询某个导演最受欢迎的电影,包括:电影名称、描述、发行时间、演员列表(姓名、年龄、参演过的最受欢迎电影),请以 JSON 格式返回。
期望返回的 JSON 结构如下:
{
"name": "电影名称",
"description": "电影描述",
"publishDate": "2023-01-01",
"performers": [
{
"name": "演员姓名",
"age": 30,
"films": ["电影 A", "电影 B"]
}
]
}
通过提示词让大模型返回 JSON 结构不够稳定,可能出现 JSON 结构错误、缺失符号等情况。除非选择的大模型不支持 JSON Mode 方式,否则不推荐此方案。
2. JSON Mode 方案
JSON Mode 使用简单,直接在请求时开启即可。该方式可靠性高,但返回性能略低于普通模式,因为模型需额外处理 JSON 约束。
各平台配置示例:
- OpenAI
OpenAiChatModel.builder()
.responseFormat("json_object")
.build();
- Azure OpenAI
AzureOpenAiChatModel.builder()
.responseFormat(new ChatCompletionsJsonResponseFormat())
.build();
- Vertex AI Gemini
VertexAiGeminiChatModel.builder()
.responseMimeType("application/json")
.build();
- Mistral AI
MistralAiChatModel.builder()
.responseFormat(MistralAiResponseFormatType.JSON_OBJECT)
.build();
- Ollama
OllamaChatModel.builder()
.format("json")
.build();
注意:该方式仍需写好提示词。虽然可靠性无问题,但在流式输出场景下,若中间截断可能导致 JSON 不完整。
3. JSON Schema 方案
当 JSON 结构或校验逻辑足够复杂时,自然语言描述显得力不从心。JSON Schema 能约定结构、数据类型、文本规则等,是大模型支持较好的方式。Spring AI 框架的结构化输出源码分析显示其使用了 JSON Schema 方案。
Schema 生成逻辑(Java/Spring AI):
private void generateSchema() {
JacksonModule jacksonModule = new JacksonModule();
SchemaGeneratorConfigBuilder configBuilder = new SchemaGeneratorConfigBuilder(DRAFT_2020_12, PLAIN_JSON)
.with(jacksonModule);
SchemaGeneratorConfig config = configBuilder.build();
SchemaGenerator generator = new SchemaGenerator(config);
JsonNode jsonNode = generator.generateSchema(this.typeRef.getType());
ObjectWriter objectWriter = new ObjectMapper().writer(new DefaultPrettyPrinter()
.withObjectIndenter(new DefaultIndenter().withLinefeed(System.lineSeparator())));
try {
this.jsonSchema = objectWriter.writeValueAsString(jsonNode);
} catch (JsonProcessingException e) {
logger.error("Could not pretty print json schema for jsonNode: " + jsonNode);
throw new RuntimeException("Could not pretty print json schema for " + this.typeRef, e);
}
}
拼接提示词:
public String getFormat() {
String template = """
Your response should be in JSON format.
Do not include any explanations, only provide a RFC8259 compliant JSON response following this format without deviation.
Do not include markdown code blocks in your response.
Remove the ```json markdown from the output.
Here is the JSON Schema instance your output must adhere to:
```%s```
""";
return String.format(template, this.jsonSchema);
}
JSON Schema 示例:
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"name": { "type": "string" },
"description": { "type": "string" },
"publishDate": { "type": "string" },
"performers": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": { "type":
每个字段可加上 description 加强大模型理解。内容虽不够紧凑,可能增加 Token 消耗,但对 Java 开发者是较好选择。
4. TypeScript DSL 方案
使用 TypeScript 语法约束 DSL,AI 的理解能力和生成效果非常稳定。TS 定义的 DSL 体积通常比 JSON Schema 更小。
export interface Film {
name: string;
description: string;
publishDate: date;
performers: {
name: string;
sex: '男' | '女';
}[];
}
Prompt 示例:请返回 xxx 导演最受欢迎的电影,请严格按照 TypeScript DSL 返回。
5. 流式输出与替代格式
在 Web 交互式场景中,需要流式返回时,JSON 结构容易被破坏导致无法完成展示。此时可考虑其他格式。
- YAML / Markdown:拥有更小的体积,且支持流式解析。对于非严格结构化需求,YAML 或 Markdown 表格形式可作为备选。
- 流式处理策略:
- Buffering:接收完整流后再解析,避免中间状态错误。
- Partial Parsing:使用支持增量解析的库(如 Streaming JSON Parser)。
- Fallback:若 JSON 解析失败,降级为文本摘要或 YAML 格式。
6. 生产环境最佳实践
在实际工程中,仅靠模型输出是不够的,还需增加以下保障:
- 响应验证:使用强类型库(如 Java 的 Jackson/Bean Validation)对返回结果进行二次校验,确保符合预期 Schema。
- 重试机制:当解析失败或格式错误时,自动触发重试,并记录错误日志以便分析。
- 超时控制:设置合理的调用超时时间,防止长尾延迟影响用户体验。
- 成本优化:对于不需要复杂结构的场景,减少 System Prompt 中的冗余信息,降低 Token 消耗。
总结
一般推荐使用:JSON Mode + Prompt + TypeScript/Schema 方式。因为 JSON Mode 是大模型原生支持的,最可靠。
如果有特殊需求,如流式输出且对结构要求不严,可采用:YAML + Prompt + TypeScript 方式。
在思考技术方案时,思维要发散,多种技术结合,才能有创新。根据具体业务场景(同步/异步、流式/非流式、成本敏感度)灵活选择最适合的数据返回格式。