16.人工智能实战:大模型回答格式总是不稳定?JSON Schema 约束、重试修复与结构化输出完整方案
人工智能实战大模型回答格式总是不稳定JSON Schema 约束、重试修复与结构化输出完整方案一、问题场景AI 回答内容对了但系统解析失败在很多 AI 应用中模型不是只负责聊天而是要输出结构化结果。例如1. 从简历中抽取姓名、学历、技能 2. 从合同中抽取甲方、乙方、金额 3. 对客服对话做分类 4. 根据用户需求生成任务 JSON 5. Agent 调用工具前生成参数这类场景最怕的问题是模型回答看起来对但不是合法 JSON。真实线上问题包括1. 多输出了一段解释文字 2. JSON 少了逗号 3. 字段名不一致 4. 数组变成字符串 5. null、空字符串、缺字段混用 6. 中文引号导致解析失败例如我们希望模型输出{name:张三,skills:[Python,FastAPI],level:senior}但模型可能输出以下是提取结果 { 姓名: 张三, 技能: Python, FastAPI }对人来说能看懂对系统来说就是失败。这篇文章解决的问题是如何让大模型稳定输出可解析、可校验、可修复的结构化 JSON。二、错误做法只在 Prompt 里说“请输出 JSON”很多人第一版会这样写请严格输出 JSON不要输出多余内容。这在简单场景下有效但线上不稳定。因为模型仍然可能1. 加解释 2. 字段漏掉 3. 类型错误 4. 输出 Markdown 代码块 5. 输出不符合业务枚举所以结构化输出不能只靠 Prompt。需要完整链路Prompt 约束 ↓ JSON 解析 ↓ Schema 校验 ↓ 失败修复 ↓ 兜底处理三、目标结构定义假设我们要做简历信息抽取。期望输出{name:张三,education:本科,skills:[Python,FastAPI,PyTorch],years_of_experience:5,level:senior}字段规则name字符串 education字符串 skills字符串数组 years_of_experience整数 level只能是 junior / middle / senior四、项目结构structured-output-demo/ ├── app.py ├── schema.py ├── parser.py ├── prompt.py └── repair.py安装依赖pipinstallfastapi uvicorn pydantic五、定义 SchemafrompydanticimportBaseModel,FieldfromtypingimportList,LiteralclassResumeExtractResult(BaseModel):name:strField(...,description候选人姓名)education:strField(...,description最高学历)skills:List[str]Field(...,description技能列表)years_of_experience:intField(...,ge0,le50)level:Literal[junior,middle,senior]这个 Schema 很关键。它不只是类型声明也是后续校验标准。六、构造强约束 Promptdefbuild_extract_prompt(resume_text:str):returnf 你是一个信息抽取助手。 请从简历文本中抽取信息并只输出合法 JSON。 【字段要求】 {{ name: 字符串候选人姓名, education: 字符串最高学历, skills: [字符串数组技能列表], years_of_experience: 整数工作年限, level: 只能是 junior / middle / senior }} 【规则】 1. 只能输出 JSON不要输出解释。 2. 不要使用 Markdown 代码块。 3. skills 必须是数组。 4. years_of_experience 必须是整数。 5. level 只能是 junior、middle、senior。 6. 如果信息缺失请根据文本保守判断不要编造。 【简历文本】{resume_text}这里要注意字段要求和规则要分开写。模型更容易遵守。七、JSON 解析器 parser.pyimportjsonimportredefextract_json(text:str):texttext.strip()# 去掉 markdown 代码块texttext.replace(json,).replace(,).strip()# 如果前后有解释尝试截取 JSON 对象matchre.search(r\{.*\},text,re.S)ifnotmatch:raiseValueError(No JSON object found)json_textmatch.group(0)returnjson.loads(json_text)这个解析器解决模型多输出解释 模型输出 Markdown 代码块八、Schema 校验fromschemaimportResumeExtractResultfromparserimportextract_jsondefparse_and_validate(raw_output:str):dataextract_json(raw_output)resultResumeExtractResult(**data)returnresult测试raw json{name:张三,education:本科,skills:[Python,FastAPI],years_of_experience:5,level:senior}“”result parse_and_validate(raw)print(result)--- ## 九、失败修复 repair.py 真实项目中第一次输出不合法很常见。 这时不要直接失败可以让模型修复。 python def build_repair_prompt(raw_output: str, error: str): return f 下面是一个模型输出的 JSON但它不符合要求。 【错误信息】 {error} 【原始输出】 {raw_output} 请修复为合法 JSON并满足以下要求 {{ name: 字符串, education: 字符串, skills: [字符串数组], years_of_experience: 整数, level: junior / middle / senior }} 只输出修复后的 JSON不要解释。 解析流程defrobust_parse(raw_output:str,llm_call):try:returnparse_and_validate(raw_output)exceptExceptionase:repair_promptbuild_repair_prompt(raw_output,str(e))repairedllm_call(repair_prompt)returnparse_and_validate(repaired)十、完整 FastAPI 示例fromfastapiimportFastAPI,HTTPExceptionfrompydanticimportBaseModelfrompromptimportbuild_extract_promptfromschemaimportResumeExtractResultfromparserimportparse_and_validatefromrepairimportbuild_repair_prompt appFastAPI(titleStructured Output Demo)classExtractRequest(BaseModel):resume_text:strdefmock_llm(prompt:str):return { name: 张三, education: 本科, skills: [Python, FastAPI, PyTorch], years_of_experience: 5, level: senior } app.post(/extract,response_modelResumeExtractResult)defextract(req:ExtractRequest):promptbuild_extract_prompt(req.resume_text)raw_outputmock_llm(prompt)try:resultparse_and_validate(raw_output)returnresultexceptExceptionase:repair_promptbuild_repair_prompt(raw_output,str(e))repaired_outputmock_llm(repair_prompt)try:returnparse_and_validate(repaired_output)exceptExceptionasfinal_error:raiseHTTPException(status_code500,detailfparse failed:{str(final_error)})启动uvicorn app:app--port8000请求curl-XPOSThttp://127.0.0.1:8000/extract\-HContent-Type: application/json\-d{ resume_text: 张三本科学历5年Python开发经验熟悉FastAPI和PyTorch。 }十一、验证结果优化前模型输出不稳定 JSON 解析经常失败 字段类型不一致优化后Prompt 限制输出 Parser 提取 JSON Pydantic 校验类型 Repair 修复错误 接口返回稳定结构这套方案的核心是不要相信模型一次输出一定正确。十二、踩坑记录坑 1只靠 PromptPrompt 能减少错误但不能消除错误。必须有程序校验。坑 2没有类型校验JSON 能解析不代表业务正确。例如{skills:Python, FastAPI}这是合法 JSON但不是你要的结构。坑 3字段枚举不限制分类任务必须限制枚举值。否则模型可能输出高级 资深 Senior Engineer系统很难处理。坑 4修复无限重试最多重试 1~2 次。否则模型异常时会拖垮系统。坑 5错误样本不沉淀每次解析失败都应该记录原始输入 模型输出 错误信息 修复结果这些是后续优化 Prompt 和 Schema 的关键数据。十三、适合收藏的结构化输出 ChecklistPrompt [ ] 是否明确只输出 JSON [ ] 是否给出字段示例 [ ] 是否限制枚举值 [ ] 是否禁止 Markdown 解析 [ ] 是否去掉代码块 [ ] 是否能提取 JSON 对象 [ ] 是否处理多余解释文本 校验 [ ] 是否使用 Schema [ ] 是否校验字段类型 [ ] 是否校验枚举值 [ ] 是否校验数值范围 修复 [ ] 是否支持一次修复 [ ] 是否限制重试次数 [ ] 是否记录失败样本 上线 [ ] 是否有兜底策略 [ ] 是否有错误率监控 [ ] 是否有坏例回收机制十四、经验总结结构化输出是大模型落地中非常关键的一环。聊天场景里模型多说几句影响不大。但系统集成场景里模型输出必须可解析 可校验 可修复 可监控一句话总结大模型结构化输出不能只靠“请输出JSON”必须靠工程链路兜住。十五、优化建议后续可以继续做1. 使用 function calling / tools 2. 使用 JSON mode 3. 增加字段级置信度 4. 对高风险字段二次校验 5. 建立结构化输出评测集 6. 将失败样本自动回流 Prompt 优化 7. 对不同业务定义不同 Schema最后一句经验模型负责生成工程负责兜底。两者缺一不可。