在这里插入图片描述

大模型输出 JSON 不完整怎么办?从 Prompt 问题到工程稳定性治理

前言

在大模型应用落地过程中,很多系统都会遇到一个非常典型的问题:

明明已经在 Prompt 里要求模型“严格输出 JSON”,但模型还是经常输出不完整、格式错误、字段缺失,甚至输出到一半就停了。

例如:

{
  "projectName": "某某项目",
  "budget": "120万元",
  "requirements": [
    "投标人须具备相关资质",
    "项目负责人须具备相关经验"

这个 JSON 明显没有闭合,程序无法直接 JSON.parse

更麻烦的是,有些 JSON 虽然语法合法,但内容并不完整:

{
  "projectName": "某某项目",
  "budget": null,
  "requirements": []
}

它可以被正常解析,却丢失了关键业务信息。

很多人第一反应是优化 Prompt,比如反复强调:

请严格输出 JSON,不要输出解释,不要输出 markdown,不要遗漏字段。

这些提示当然有用,但它们只能降低错误概率,不能从根本上解决问题。

因为大模型输出 JSON 不完整,本质上不是一个单纯的 Prompt 问题,而是一个工程稳定性问题。

要真正解决它,需要从模型调用、输出协议、Schema 设计、校验修复、重试补偿、任务拆分等多个层面共同治理。


一、问题本质:大模型不是 JSON 序列化器

在传统程序里,JSON 通常是由代码序列化生成的。

例如:

JSON.stringify(data)

这种 JSON 输出天然具备结构完整性。

但大模型不是在执行 JSON.stringify,而是在逐 token 生成文本。

这意味着它可能出现:

  • 生成到一半达到输出长度上限;
  • 生成过程中偏离结构;
  • 忘记闭合数组或对象;
  • 字符串引号不完整;
  • 输出了额外解释性文字;
  • 字段漏掉;
  • 数组内容少了一部分;
  • 内容看似合理但不符合业务约束。

所以,大模型输出 JSON 的正确理解应该是:

模型负责“生成候选结构化内容”,工程系统负责“验证、修复、重试、合并和入库”。

不能把大模型直接当成稳定的 JSON 序列化器。


在这里插入图片描述

二、典型问题分类

大模型输出 JSON 不完整,通常可以分为两类。

1. 语法不完整

这类问题比较明显,程序一解析就会报错。

例如:

{
  "title": "文件上传一致性治理",
  "tags": ["大模型", "工程化", "JSON"

常见表现包括:

  • 少了右大括号 }
  • 少了右中括号 ]
  • 字符串没有闭合;
  • JSON 中夹杂了 markdown;
  • 输出了多余的自然语言;
  • 多了尾逗号;
  • 使用了非法注释;
  • 使用了单引号;
  • key 没有加双引号。

这类问题一般可以通过 JSON Repair、格式清洗、模型重试来解决。

2. 内容不完整

这类问题更隐蔽。

例如要求模型输出:

{
  "basicInfo": {},
  "timeline": [],
  "qualificationRequirements": [],
  "scoringRules": [],
  "riskPoints": []
}

模型返回:

{
  "basicInfo": {
    "projectName": "某某项目"
  },
  "timeline": [],
  "qualificationRequirements": [],
  "scoringRules": [],
  "riskPoints": []
}

这个 JSON 是合法的,但业务上可能是错误的。

因为原文里明明有大量资质要求、评分规则和风险点,模型却没有抽取出来。

所以,JSON 完整性不能只看语法,还要看业务字段是否完整。


三、为什么大模型会输出不完整 JSON

1. 输出内容太长,被截断

这是最常见原因。

当你要求模型一次性输出一个很大的 JSON,例如:

{
  "documents": [
    {
      "title": "...",
      "sections": [
        {
          "heading": "...",
          "clauses": [
            "...",
            "...",
            "..."
          ]
        }
      ]
    }
  ]
}

如果数组很长、字段很多、内容很大,模型很容易在输出中途停止。

这时即使 Prompt 写得再严格,也挡不住输出被截断。

本质原因是:单次模型响应有输出长度限制。

2. 输入上下文太长,压缩了输出空间

很多业务场景会把大量内容塞给模型,例如:

  • 招投标公告;
  • 合同文本;
  • 法律条款;
  • 网页正文;
  • OCR 结果;
  • 日志文件;
  • 多轮对话记录;
  • RAG 检索上下文。

输入越长,留给输出的空间就越少。

如果你把十几页文档塞进去,又要求模型输出一个完整复杂 JSON,就很容易出现后半截缺失。

3. Schema 设计过于复杂

有些 JSON Schema 嵌套太深:

{
  "project": {
    "basic": {},
    "buyer": {},
    "supplier": {},
    "timeline": [],
    "qualification": {
      "company": [],
      "personnel": [],
      "performance": [],
      "financial": []
    },
    "scoring": {
      "business": [],
      "technical": [],
      "price": []
    },
    "risks": []
  }
}

这类结构虽然看起来规范,但对模型输出稳定性并不友好。

Schema 越复杂,模型越容易:

  • 漏字段;
  • 放错层级;
  • 数组对象结构不一致;
  • 部分字段为空;
  • 嵌套括号没有闭合。

4. 只靠 Prompt,没有程序校验

很多系统只做了这一步:

请严格输出 JSON。

然后就直接把模型输出传给业务系统。

这是非常危险的。

因为 Prompt 只是一种软约束,不能提供工程级保证。

只要模型输出进入业务系统,就必须经过程序化校验。


四、整体治理思路

大模型 JSON 输出不稳定,不能靠单点优化解决,而应该设计一条完整的输出治理链路。

图 1:大模型 JSON 输出治理链路

成功

失败

通过

失败

通过

不通过

原始输入文本

文本清洗与分块

模型结构化抽取

格式清洗

JSON.parse

Schema 校验

JSON Repair

业务完整性校验

模型重试

入库或下游处理

补充抽取 / 分块重试 / 人工审核

这条链路的核心思想是:

模型输出不是终点,而是候选结果。
候选结果必须经过解析、校验、修复、重试和业务验收。

推荐的治理顺序是:

结构化输出 > 拆分任务 > Schema 校验 > 自动修复 > 重试补偿 > 人工兜底

五、第一层治理:优先使用结构化输出能力

如果使用的模型平台支持结构化输出、函数调用、工具调用或 JSON Schema 约束,应该优先使用这些能力。

不要只在 Prompt 里写:

请输出 JSON。

而应该显式定义输出结构。

例如抽取文章信息,可以定义为:

{
  "title": "string",
  "summary": "string",
  "keywords": ["string"],
  "sections": [
    {
      "heading": "string",
      "summary": "string"
    }
  ]
}

结构化输出的优势是:

  • 减少非法 JSON;
  • 限制模型输出范围;
  • 降低额外解释文字;
  • 提高字段稳定性;
  • 方便后端做 Schema 校验。

但要注意:

结构化输出只能提升格式稳定性,不能保证业务内容一定完整。

例如模型可以稳定输出:

{
  "qualificationRequirements": []
}

但这不代表原文里真的没有资质要求。

所以结构化输出后,仍然需要业务完整性校验。


六、第二层治理:不要一次性生成大 JSON

这是最重要的一条工程经验。

很多 JSON 不完整问题,根源都是一次性输出太多。

错误做法:

请从这篇长文档中抽取所有信息,并输出完整 JSON。

推荐做法:

第一步:只抽取基础信息
第二步:只抽取时间节点
第三步:只抽取资质要求
第四步:只抽取评分规则
第五步:只抽取风险点
第六步:由程序合并最终 JSON

图 2:从“大 JSON”改成“小 JSON 合并”

长文档

基础信息抽取

时间节点抽取

资质要求抽取

评分规则抽取

风险点抽取

程序合并

最终结构化结果

例如,不要让模型一次性输出:

{
  "basicInfo": {},
  "timeline": [],
  "qualificationRequirements": [],
  "scoringRules": [],
  "riskPoints": []
}

而是拆成多个小 JSON:

{
  "basicInfo": {
    "projectName": "xxx",
    "budget": "xxx",
    "buyer": "xxx"
  }
}
{
  "timeline": [
    {
      "name": "报名截止时间",
      "date": "2026-07-10"
    }
  ]
}
{
  "qualificationRequirements": [
    "要求一",
    "要求二"
  ]
}

最后由后端程序合并,而不是让模型一次性输出一个巨大结构。

这样做有几个好处:

  • 单次输出更短;
  • JSON 更容易闭合;
  • 单块失败可以单独重试;
  • 不同字段可以使用不同 Prompt;
  • 后端合并过程更可控;
  • 更适合生产环境排查问题。

七、第三层治理:数组内容分页输出

如果某个字段本身就是长数组,例如:

  • 合同条款;
  • 招标要求;
  • 评分规则;
  • 风险点;
  • 表格行;
  • 网页列表;
  • 商品信息;
  • 日志事件;
  • 审计问题。

不建议一次性让模型输出全部数组。

推荐使用分页协议。

例如:

{
  "page": 1,
  "pageSize": 20,
  "hasMore": true,
  "items": [
    {
      "index": 1,
      "content": "..."
    }
  ]
}

下一次请求:

请继续输出第 2 页,只输出 JSON,不要重复第 1 页内容。

返回:

{
  "page": 2,
  "pageSize": 20,
  "hasMore": false,
  "items": [
    {
      "index": 21,
      "content": "..."
    }
  ]
}

图 3:长数组分页抽取

数据库 大模型 应用服务 数据库 大模型 应用服务 抽取第 1 页 items page=1, hasMore=true 保存第 1 页结果 抽取第 2 页 items page=2, hasMore=true 保存第 2 页结果 抽取第 3 页 items page=3, hasMore=false 合并并标记完成

分页抽取的本质是:

让模型每次只完成一个小而确定的输出任务。

这比单次生成超大 JSON 稳定得多。


八、第四层治理:后端必须做 Schema 校验

无论 Prompt 多严格,后端都必须校验模型输出。

一个基本的处理流程应该是:

1. 接收模型原始输出
2. 清理 markdown 代码块
3. 尝试 JSON.parse
4. 使用 Schema 校验字段
5. 校验业务必填项
6. 失败则修复或重试
7. 通过后再入库

以 TypeScript 为例,可以使用 Zod:

import { z } from "zod";

const ExtractResultSchema = z.object({
  title: z.string().nullable(),
  summary: z.string().nullable(),
  keywords: z.array(z.string()),
  risks: z.array(z.string()),
});

function parseAndValidate(raw: string) {
  const cleaned = cleanModelOutput(raw);
  const parsed = JSON.parse(cleaned);
  return ExtractResultSchema.parse(parsed);
}

其中 cleanModelOutput 可以处理模型常见输出问题:

function cleanModelOutput(raw: string): string {
  return raw
    .trim()
    .replace(/^```json\s*/i, "")
    .replace(/^```\s*/i, "")
    .replace(/```$/i, "")
    .trim();
}

Schema 校验至少要解决三类问题:

校验类型 目标
语法校验 JSON 是否能 parse
结构校验 字段类型是否正确
业务校验 字段是否满足业务完整性

只做 JSON.parse 是不够的。

因为下面这个 JSON 可以 parse,但业务上可能不可接受:

{
  "title": null,
  "summary": null,
  "keywords": [],
  "risks": []
}

九、第五层治理:JSON Repair 只能修语法,不能修业务

当模型输出 JSON 语法错误时,可以使用 JSON Repair 类工具尝试修复。

例如模型输出:

{
  "title": "大模型工程化",
  "tags": ["LLM", "JSON", "结构化输出"

Repair 之后可能变成:

{
  "title": "大模型工程化",
  "tags": ["LLM", "JSON", "结构化输出"]
}

这对语法错误很有帮助。

但是要注意:

JSON Repair 只能修语法,不能保证内容完整。

如果模型本来只输出了一半,Repair 工具只是帮你补上括号,让它变成合法 JSON。

但业务内容仍然可能缺失。

所以修复后必须继续做:

  • Schema 校验;
  • 必填字段校验;
  • 数组长度校验;
  • 内容覆盖率校验;
  • 是否疑似截断判断;
  • 与原文的引用关系校验。

一个常见策略是:

JSON.parse 失败
  ↓
JSON Repair
  ↓
再次 JSON.parse
  ↓
Schema 校验
  ↓
业务完整性校验
  ↓
仍失败则重试

图 4:JSON 修复不是终点

模型原始输出

JSON.parse 成功?

Schema 校验

JSON Repair

修复后 parse 成功?

重新调用模型

业务完整性通过?

结果可用


十、第六层治理:重试要有策略,不能盲目重试

很多系统遇到模型输出不完整后,会简单重试一次。

但如果不分析失败原因,盲目重试可能效果很差。

推荐按错误类型设计不同重试策略。

错误类型 重试策略
JSON 语法错误 降低输出复杂度,要求只输出修复后的 JSON
字段缺失 针对缺失字段单独补充抽取
数组截断 使用分页或 continuation 协议
内容为空 换 Prompt 或提供更小上下文
Schema 不匹配 强化字段类型要求
多次失败 进入人工审核或降级流程

例如字段缺失时,不要重新抽取全部内容,而是只抽缺失字段:

上一次抽取结果中 qualificationRequirements 为空。
请只从原文中抽取资质要求。
只输出如下 JSON:
{
  "qualificationRequirements": []
}

这样比重新生成整个大 JSON 更稳定。


十一、第七层治理:设计 continuation 断点续写协议

如果 JSON 已经被截断,也可以设计续写机制。

但不要简单说:

继续。

因为模型可能会:

  • 从头开始输出;
  • 输出重复内容;
  • 忘记之前的结构;
  • 输出解释性文字;
  • 继续生成非法 JSON。

更好的方式是设计明确的 continuation 协议。

例如:

{
  "continueFrom": 21,
  "items": []
}

请求模型:

上一次输出在 items 第 20 项后被截断。
请从第 21 项开始继续。
不要重复前 20 项。
只输出 continuation JSON:
{
  "continueFrom": 21,
  "items": []
}

后端拿到结果后,不是直接拼接字符串,而是解析 JSON 后把 items 追加到已有数组。

注意:

不推荐直接拼接两段 JSON 字符串,推荐用结构化 continuation 结果做程序合并。


十二、第八层治理:将“大模型抽取”变成可观测任务

在生产系统里,模型 JSON 输出失败不能只体现在日志里。

建议把每次抽取任务都记录下来。

至少记录:

  • taskId;
  • model;
  • promptVersion;
  • inputHash;
  • schemaVersion;
  • rawOutput;
  • cleanedOutput;
  • parseStatus;
  • validationStatus;
  • retryCount;
  • errorType;
  • errorMessage;
  • finalStatus;
  • createdAt;
  • updatedAt。

这样可以回答几个关键问题:

  • 哪类文档最容易失败?
  • 哪个字段最容易缺失?
  • 哪个 Prompt 版本质量更好?
  • 哪个模型输出 JSON 更稳定?
  • 是输入太长导致失败,还是 Schema 太复杂?
  • 重试成功率是多少?
  • Repair 成功率是多少?

图 5:结构化抽取任务的可观测闭环

模型调用

原始输出记录

解析状态

Schema 校验状态

业务校验状态

重试记录

最终结果

质量报表

Prompt / Schema / 分块策略优化

没有可观测性,就很难持续提升结构化输出稳定性。


十三、推荐的工程架构

在真实系统中,可以把大模型结构化输出设计成一个独立的 Extractor Pipeline。

图 6:LLM 结构化抽取 Pipeline 架构

业务输入

Input Normalizer
输入清洗

Chunker
文本分块

Prompt Builder
提示词构建

LLM Client
模型调用

Output Cleaner
输出清洗

JSON Parser
解析器

Schema Validator
结构校验

Business Validator
业务校验

是否通过?

Result Merger
结果合并

Retry Or Repair
修复重试

Final Result
最终结果

Storage / Downstream
入库或下游处理

每个模块职责如下:

模块 作用
Input Normalizer 清洗 HTML、OCR 噪声、特殊字符
Chunker 将长文本拆成可控片段
Prompt Builder 根据任务类型和 Schema 构造 Prompt
LLM Client 调用模型并控制参数
Output Cleaner 去除 markdown、无关解释
JSON Parser 解析 JSON
Schema Validator 校验类型和结构
Business Validator 校验字段业务完整性
Retry Or Repair 修复、重试、补充抽取
Result Merger 合并多个小 JSON
Storage 保存最终结构化结果

这套架构的关键不是“某个 Prompt 写得多好”,而是把模型输出当成一个不稳定外部依赖来治理。


十四、业务完整性校验怎么做

业务完整性校验通常比 JSON 语法校验更重要。

以文档信息抽取为例,可以设计以下规则:

1. 必填字段校验

例如:

title 不能为空
source 不能为空
publishDate 不能为空

如果为空,就标记为字段缺失。

2. 数组最小长度校验

例如:

如果原文中出现“资格要求”“投标人须具备”等关键词,
qualificationRequirements 不应该为空。

3. 内容引用校验

要求模型输出每个结论时附带来源片段:

{
  "value": "投标人须具备建筑工程施工总承包三级及以上资质",
  "evidence": "原文引用片段..."
}

这样可以降低幻觉,也方便人工审核。

4. 覆盖率校验

对于分块抽取,可以检查:

每个 chunk 是否都被处理
每个 chunk 是否都有结果
是否存在异常空结果

5. 异常值校验

例如:

  • 日期格式不合法;
  • 金额单位异常;
  • 电话号码格式异常;
  • 数组项重复率过高;
  • 输出内容和原文语言不一致;
  • 字段内容明显跑题。

这些业务校验可以帮助系统发现“合法但不可用”的 JSON。


十五、Prompt 设计建议

虽然 Prompt 不能解决所有问题,但好的 Prompt 仍然很重要。

一个比较稳的 Prompt 可以这样设计:

你是一个结构化信息抽取器。

任务:
从给定文本中抽取指定字段,并严格输出 JSON。

要求:
1. 只输出 JSON,不要输出 markdown。
2. 不要使用 ```json 代码块。
3. 所有字段必须出现。
4. 不确定的字段填 null。
5. 数组没有内容时返回 []。
6. 不要省略字段。
7. 不要输出解释性文字。
8. 不要编造原文不存在的信息。
9. 每个抽取结果尽量保留原文表达。
10. 输出必须符合指定 Schema。

Schema:
{
  "title": "string | null",
  "summary": "string | null",
  "keywords": "string[]",
  "risks": "string[]"
}

待抽取文本:
{{input}}

对于复杂任务,还可以进一步要求:

如果字段无法从原文找到,请填 null 或 [],不要猜测。
如果数组内容超过 20 条,只返回前 20 条,并设置 hasMore=true。

但仍然要记住:

Prompt 是第一层防线,程序校验才是最后一道防线。


十六、参数设置也会影响 JSON 稳定性

模型调用参数也会影响结构化输出质量。

一般来说,结构化抽取任务不需要太强创造性,建议:

  • 降低 temperature;
  • 控制 max output tokens;
  • 对长文本先分块;
  • 尽量使用结构化输出模式;
  • 避免在同一次调用里要求模型做太多任务;
  • 不要把“抽取、总结、判断、改写、生成建议”混在一个 JSON 里。

一个典型错误是:

请抽取信息、总结文章、分析风险、生成建议、输出完整 JSON。

这会显著增加失败概率。

更好的做法是拆成多个任务:

抽取信息
总结内容
分析风险
生成建议

每个任务输出一个小 JSON。


十七、生产环境推荐处理流程

综合来看,一个比较成熟的生产级处理流程如下:

1. 输入清洗
2. 文本分块
3. 每个分块独立抽取小 JSON
4. 模型使用结构化输出或严格 Schema
5. 清洗模型输出
6. JSON.parse
7. JSON Repair
8. Schema 校验
9. 业务完整性校验
10. 失败按错误类型重试
11. 多个小 JSON 合并
12. 合并后去重、排序、归一化
13. 最终结果二次校验
14. 入库
15. 记录任务日志和质量指标

可以概括为:

不要追求一次模型调用解决所有问题,
而要把结构化输出设计成一条可恢复、可重试、可观测的工程链路。

十八、一个完整的 TypeScript 示例

下面是一个简化版的工程处理流程。

import { z } from "zod";

const ResultSchema = z.object({
  title: z.string().nullable(),
  summary: z.string().nullable(),
  keywords: z.array(z.string()),
  risks: z.array(z.string()),
});

type ExtractResult = z.infer<typeof ResultSchema>;

function cleanOutput(raw: string): string {
  return raw
    .trim()
    .replace(/^```json\s*/i, "")
    .replace(/^```\s*/i, "")
    .replace(/```$/i, "")
    .trim();
}

async function repairJson(raw: string): Promise<string> {
  // 实际项目中可以接入 json repair 库,或者调用模型做修复
  return raw;
}

async function callModel(input: string): Promise<string> {
  // 调用大模型
  return "";
}

function businessValidate(result: ExtractResult) {
  const errors: string[] = [];

  if (!result.title) {
    errors.push("title is empty");
  }

  if (!result.summary) {
    errors.push("summary is empty");
  }

  return {
    ok: errors.length === 0,
    errors,
  };
}

async function extractWithRetry(input: string, maxRetry = 2) {
  let lastError: unknown;

  for (let i = 0; i <= maxRetry; i++) {
    try {
      const raw = await callModel(input);
      const cleaned = cleanOutput(raw);

      let parsed: unknown;

      try {
        parsed = JSON.parse(cleaned);
      } catch {
        const repaired = await repairJson(cleaned);
        parsed = JSON.parse(repaired);
      }

      const result = ResultSchema.parse(parsed);
      const businessCheck = businessValidate(result);

      if (!businessCheck.ok) {
        throw new Error(
          `Business validation failed: ${businessCheck.errors.join(", ")}`
        );
      }

      return result;
    } catch (error) {
      lastError = error;
    }
  }

  throw lastError;
}

这个示例体现了几个关键点:

  • 不直接信任模型输出;
  • 先清洗;
  • 再解析;
  • 解析失败尝试修复;
  • 修复后继续 Schema 校验;
  • Schema 通过后还要做业务校验;
  • 失败可以重试;
  • 重试仍失败要抛出明确错误。

十九、最佳实践总结

1. 不要让模型一次性输出巨大 JSON

只要 JSON 大到你担心它会不会完整,就说明它应该被拆分。

2. 复杂任务分阶段完成

抽基础信息、抽列表、抽风险、做总结,最好分开调用。

3. 长数组分页输出

不要让模型一次输出几百条数组项。

4. 使用结构化输出能力

有 JSON Schema、Function Calling、Tool Calling 能力时优先使用。

5. 后端必须做 Schema 校验

模型输出不能直接入库。

6. JSON Repair 只是语法兜底

它不能保证业务内容完整。

7. 重试要按错误类型设计

字段缺失就补字段,数组截断就分页,不要无脑全量重试。

8. 关键字段要带 evidence

结构化抽取最好要求模型返回原文证据,方便校验和人工审核。

9. 抽取任务要可观测

记录 raw output、parse status、schema version、retry count、error type。

10. 把模型当成不稳定外部依赖

像治理第三方接口一样治理模型输出。


二十、结语

大模型输出 JSON 不完整,是 AI 应用工程化中非常典型的问题。

它表面上看是模型“不听话”或者 Prompt“不够严格”,但本质上是系统没有为不稳定输出建立足够的工程防线。

真正可靠的方案不是反复强调“请严格输出 JSON”,而是建立一套完整机制:

结构化输出
任务拆分
分页协议
Schema 校验
业务校验
JSON Repair
错误分类
自动重试
断点续写
结果合并
任务观测
人工兜底

当这套机制建立起来后,大模型就不再是一个不可控的文本生成器,而会变成一个可以被工程系统约束、校验、修复和治理的结构化信息抽取组件。

一句话总结:

不要把大模型当 JSON 序列化器,要把它当信息抽取器;JSON 的完整性、合法性和可靠性,必须由工程系统兜底。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐