news 2026/9/3 8:01:32

大模型JSON结构化输出全指南:从原理到简历助手实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
大模型JSON结构化输出全指南:从原理到简历助手实战

你是不是也有过这样的经历:让大模型帮忙整理一段简历信息,结果它给你输出一大段带小标题、加粗、列表混排的“精美文案”,你反而要花更多时间去字段里捞数据?如果大模型能直接返回一份干净的 JSON,任务就简单多了——程序可以直接读取、校验、落库,前端也能直接渲染。这正是 JSON 结构化输出的价值。

本文将围绕“JSON 结构化输出”这个核心主题展开,先讲清为什么需要结构化输出,再介绍 JSON 的基础语法和常见误区,接着剖析大模型产生杂乱输出的原因,最后用一个完整的“简历助手”项目,演示从定义数据结构、构造 Prompt、调用大模型,到解析校验和生成标准简历文案的全流程。

文章适合以下读者:

  • 正在用大模型 API 做应用开发的工程师。
  • 被大模型“自由发挥”式输出困扰的开发者。
  • 想系统学习 JSON 与结构化数据处理的初学者。
  • 准备做 AI 项目实战、毕设或简历项目的新手。

学完之后,你能掌握一套从“自由文本”到“标准 JSON”再到“业务数据”的完整处理思路,并且可以把简历助手扩展成招聘匹配、档案整理、信息抽取等更多工具。

1. 为什么需要结构化输出

1.1 非结构化输出的痛点

大模型默认的输出方式是“自然语言”。自然语言天生适合人类阅读,却不适合程序解析。假设你让大模型帮用户提取简历里的工作经历,模型可能返回这样的内容:

张三,2019年入职北京某科技公司,担任后端开发工程师,期间负责电商系统订单模块的设计与开发,使用Java、Spring Boot等技术栈,带领3人小组,项目上线后系统QPS提升约30%,2021年晋升为高级工程师,2022年离职。

这段文本信息密度很高,但如果你的目标是“把这份工作经历存入数据库”,就会遇到几个麻烦:

  • 没有统一的字段边界。“入职时间”“离职时间”“职责描述”都混在一句话里。
  • 计算机无法直接读取。要提取“公司名称”,只能靠正则或再次调用模型。
  • 格式不稳定。换个提问方式,模型输出的措辞和结构可能完全不同。

在业务系统中,我们更希望大模型返回这样的内容:

{ "name": "张三", "jobs": [ { "company": "北京某科技公司", "start_date": "2019-07", "end_date": "2022-06", "title": "高级后端开发工程师", "description": "负责电商系统订单模块设计与开发,使用Java、Spring Boot等技术栈,带领3人小组,项目上线后系统QPS提升约30%。" } ] }

两相对比,结构化数据的优势一目了然:字段明确、程序可解析、前后端可复用、校验成本低。

1.2 结构化输出解决什么问题

结构化输出,本质上是给大模型的“自由发挥”套上一层约束。它解决的核心问题包括:

  • 数据可解析性。程序收到 JSON 后,可以直接用json.loads()等 API 转成对象,不需要写复杂的正则表达式。
  • 数据一致性。相同的字段在不同记录中拥有相同的结构,便于汇总和比较。
  • 数据可校验性。可以配合 JSON Schema 对模型输出进行自动化校验,不满足要求的字段直接重新生成。
  • 业务集成效率。结构化数据可以直接存入数据库,或传给下游接口,完成更复杂的自动化流程。

1.3 JSON 不是唯一的结构化方案

常见的大模型结构化输出格式有 JSON、XML 和 Markdown。JSON 是目前最主流的选择,因为:

  • 前端 JavaScript 原生支持 JSON 解析。
  • 后端 Java、Python、Go 等语言都有成熟的 JSON 库。
  • JSON 结构轻量,阅读性和机器可读性兼具。
  • 大部分大模型 API 都提供了 JSON 模式或结构化输出能力。

XML 在文档型数据场景也有优势,但解析成本更高,信息密度更低。Markdown 更适合展示型文本,不适合做数据交换。因此,本文以 JSON 作为主要讨论对象。

2. JSON 基础速览

在进入实战前,先快速复习 JSON 的核心语法。如果你已经很熟悉,可以直接跳到第 3 节。

2.1 JSON 的六种数据类型

JSON(JavaScript Object Notation,JavaScript 对象简谱)是一种轻量级的数据交换格式。它一共支持六种数据类型:

数据类型示例说明
字符串"name": "张三"必须用双引号包裹
数字"age": 26整数或浮点数
布尔值"is_active": true只能是小写 true/false
数组"skills": ["Java", "Python"]有序列表,值类型可以混合
对象"address": {"city": "北京"}无序键值对集合
空值"summary": null表示空

一个完整的 JSON 对象可以嵌套多种数据类型,例如:

{ "basic_info": { "name": "张三", "age": 26, "email": "zhangsan@example.com", "is_full_time": true }, "skills": ["Java", "Python", "SQL"], "work_experience": [ { "company": "某科技有限公司", "years": 2 } ] }

2.2 JSON 常见语法误区

新手在写 JSON 时最容易犯几个错误:

  • 误用单引号。JSON 字符串必须使用双引号,不能写成{'name': '张三'}
  • 多了尾逗号。数组或对象最后一个元素后面不能加逗号。
  • 注释混入 JSON。标准 JSON 不支持注释,像// 这是注释这样的内容会导致解析失败。
  • 布尔值大小写错误。JSON 里布尔值必须是小写true/false
  • 数字带前导零。例如"age": 08是非法的。

如果你经常手写 JSON,建议在 IDE 中安装 JSON 格式化插件,或者用在线 JSON 校验工具检查语法。

2.3 为什么 JSON 适合与大模型搭配使用

大模型的训练数据中有大量代码和配置文件,JSON 是其中最常见的格式之一,因此模型对 JSON 的生成能力相对较强。加上 JSON 结构本身自带明确的键名,即使模型输出的内容稍微变化,程序也能通过键名提取对应值,容错率比纯文本高很多。

举个简单的例子,下面两段 JSON 虽然字段顺序不同,但解析结果完全等价:

{"name": "张三", "age": 26}
{"age": 26, "name": "张三"}

这种无序性让模型在生成时更容易命中“正确结构”,而不是必须记住固定的顺序。

3. 大模型非结构化输出的原因与风险

3.1 模型为什么总爱“自由发挥”

大模型的本质是“文本生成器”。给定一段输入(Prompt),它会根据历史数据分布预测下一个 token(文本单元)。这种设计使得模型天然倾向于生成流畅自然的文本,而不是严格符合结构的“数据”。如果你不约束它,它很有可能会:

  • 在 JSON 前后添加解释性文字:“好的,以下是你要的 JSON:”。
  • 使用 Markdown 代码块包裹 JSON。
  • 漏掉某个字段,或把字段名改成近义词。
  • 在描述型字段里混入大量“仅供参考”的废话。

这些行为对聊天场景很友好,但对程序化调用是灾难。

3.2 解析杂乱输出的成本

假设大模型返回了这样的内容:

好的,根据你的描述,我给你整理了一份简历: { "姓名": "张三", // 注意:这里用了中文键名 "age": 26, "skills": ["Java", "Python",], } 希望这份简历能帮到你!

程序要把它变成标准 JSON,需要经过以下步骤:

  1. 从文本中截取 JSON 片段。
  2. 去掉 Markdown 代码块标记。
  3. 删除注释。
  4. 修复尾逗号。
  5. 把中文键名统一成英文键名。

每一步都可能出错。如果模型输出结构不稳定,你甚至需要写一套“解析容错层”,这会让项目复杂度急剧上升。

3.3 不安全解析会带来什么隐患

除了格式问题,如果直接把大模型输出的内容当 JSON 解析,还可能出现数据类型错误、字段缺失、注入风险等问题。例如:

  • 年龄字段可能是字符串"26",而不是数字26
  • 邮箱字段可能是null,但程序没有做空值处理。
  • 恶意 Prompt 可能诱导模型输出带有额外字段的 JSON,绕过下游的数据校验逻辑。

因此,实战中必须做到“先校验、再使用”,而不是盲目相信模型输出。

4. 结构化输出的主流实现方案

4.1 方案一:Prompt 约束 + JSON 示例

这是最基础的做法。在 Prompt 中明确告诉模型“只输出 JSON”,并在 Prompt 中给出期望的结构示例。适合没有平台级 JSON 模式支持的大模型接口。

请根据用户提供的简历信息,输出一个 JSON 对象。 JSON 结构如下: { "name": "string", "age": number, "skills": ["string"] } 不要输出任何解释或额外内容。

这种方法的优点是通用,缺点是模型有时仍不听话,需要额外解析和重试。

4.2 方案二:平台 JSON 模式

OpenAI、DeepSeek、Ollama 等大模型服务通常提供response_format参数,允许用户在请求中声明json_object或传入 JSON Schema。

例如在 OpenAI 兼容接口中:

{ "response_format": {"type": "json_object"} }

部分平台还支持:

{ "response_format": { "type": "json_schema", "json_schema": {"name": "Resume", "schema": {...}} } }

这种方式能将“模型不输出 JSON”的概率降到很低,但仍需注意字段校验。

4.3 方案三:函数调用 / 工具调用

OpenAI 等平台支持 function calling。开发者可以定义一个“工具函数”,让模型把输出包装成该函数入参对象。模型在回答时会自动生成符合函数参数结构的 JSON。

{ "name": "extract_resume", "description": "提取简历信息", "parameters": { "type": "object", "properties": { "name": {"type": "string"}, "jobs": {"type": "array"} } } }

这种方案更加工程化,适合构建 Agent 类应用。

4.4 方案四:本地后处理纠偏

如果模型服务不支持 JSON 模式,或者仍偶尔返回夹杂文本的内容,可以在代码里做一层后处理:

import json import re def extract_json(text: str) -> dict: """从模型输出中提取 JSON 对象。""" text = text.strip() # 去掉 Markdown 代码块标记 text = re.sub(r"^```(?:json)?\s*|\s*```$", "", text).strip() start = text.find("{") end = text.rfind("}") if start == -1 or end == -1: raise ValueError("未找到 JSON 对象") return json.loads(text[start:end + 1])

后处理也有局限,比如尾逗号、单引号、注释等问题仍然需要额外处理。建议用第三方库如json5或自定义修复逻辑。

5. 项目实战:简历助手——需求分析与项目准备

前面把概念讲清楚了,下面进入完整实战。我们的目标是:做一个“简历助手”,用户输入一段自由描述,程序调用大模型,输出标准的简历 JSON,并最终生成一份格式化的简历文案。

5.1 项目需求拆分

简历助手需要完成以下功能:

  1. 接收用户输入的原始简历描述。
  2. 调用大模型接口,要求模型输出符合指定 JSON Schema 的简历数据。
  3. 解析大模型返回的 JSON,并进行字段校验。
  4. 将解析后的数据保存到本地 JSON 文件。
  5. 根据数据生成一段 Markdown 格式的简历文案,方便直接复制使用。

从工程角度,这个项目可以拆成四个模块:

  • 数据模型模块:定义简历结构。
  • 大模型调用模块:拼接 Prompt、调用接口、接收结果。
  • 解析校验模块:解析 JSON、检查字段。
  • 文案生成模块:将结构化数据渲染成文案。

5.2 技术栈与运行环境

本文示例以 Python 为例,主要使用以下组件:

  • Python 3.9+。
  • requests 库,用于调用兼容 OpenAI 协议的大模型接口。
  • jsonschema 库(可选),用于更规范的 JSON 校验。
  • 大模型接口:可以使用 OpenAI、DeepSeek、Ollama 等支持 OpenAI 兼容格式的服务。

版本方面需要提醒一点:不同大模型服务的 API 参数可能有差异,比如response_format的支持程度不同。本文的代码会兼容“支持 JSON 模式”和“不支持 JSON 模式”两种场景,你可以根据实际服务调整。

如果你没有 API Key,可以用 Ollama 本地部署一个小模型,也可以先直接跳过调用步骤,用模拟数据进行项目流程调试。

5.3 创建项目结构

终端执行:

mkdir resume-assistant cd resume-assistant mkdir data output

目录结构如下:

resume-assistant/ ├── data/ # 存放输入文本和中间 JSON ├── output/ # 存放最终生成的简历 JSON 和文案 ├── config.py # 配置项 ├── models.py # 数据结构定义 ├── llm_client.py # 大模型调用模块 ├── parser.py # JSON 解析校验模块 ├── renderer.py # 简历文案渲染模块 └── main.py # 主入口

5.4 安装依赖

在项目根目录执行:

pip install requests jsonschema

requests用于发送 HTTP 请求,jsonschema用于校验 JSON 结构。如果你使用的是 OpenAI 官方 SDK,也可以换成openai库,但为了减少依赖和适配不同服务,本文使用requests直接调用 OpenAI 兼容接口,这样更通用。

6. 简历助手核心代码实现

下面逐模块编写代码。完整代码可以直接复制运行,但需要根据你的实际大模型服务地址和密钥进行配置。

6.1 配置模块 config.py

# config.py import os # 大模型服务配置 # 以 OpenAI 兼容接口为例。使用 Ollama 时,可以改为 http://localhost:11434/v1 LLM_BASE_URL = os.getenv("LLM_BASE_URL", "https://api.openai.com/v1") LLM_API_KEY = os.getenv("LLM_API_KEY", "your-api-key") LLM_MODEL = os.getenv("LLM_MODEL", "gpt-4o-mini") # 是否使用 JSON 模式。部分服务不支持,请设置为 False LLM_JSON_MODE = os.getenv("LLM_JSON_MODE", "true").lower() == "true" # 输入输出目录 INPUT_DIR = "data" OUTPUT_DIR = "output"

这里使用环境变量管理密钥,避免把 API Key 硬编码在代码里,也更符合安全实践。如果你用的是本地 Ollama,LLM_BASE_URLhttp://localhost:11434/v1,API Key 填任意非空字符串即可。

6.2 数据模型 models.py

我们定义简历结构。这里采用类属性的方式编写,方便 IDE 自动补全和后续扩展:

# models.py from dataclasses import dataclass, field from typing import List, Optional @dataclass class Education: school: Optional[str] = None degree: Optional[str] = None major: Optional[str] = None start_date: Optional[str] = None end_date: Optional[str] = None @dataclass class WorkExperience: company: Optional[str] = None title: Optional[str] = None start_date: Optional[str] = None end_date: Optional[str] = None description: Optional[str] = None @dataclass class Resume: name: Optional[str] = None age: Optional[int] = None email: Optional[str] = None phone: Optional[str] = None skills: List[str] = field(default_factory=list) education: List[Education] = field(default_factory=list) work_experience: List[WorkExperience] = field(default_factory=list) summary: Optional[str] = None

同时,我们把 JSON Schema 单独定义为常量,后续 Prompt 和校验都会用到:

# models.py 追加内容 RESUME_SCHEMA = { "type": "object", "properties": { "name": {"type": "string"}, "age": {"type": "integer"}, "email": {"type": "string"}, "phone": {"type": "string"}, "skills": { "type": "array", "items": {"type": "string"} }, "education": { "type": "array", "items": { "type": "object", "properties": { "school": {"type": "string"}, "degree": {"type": "string"}, "major": {"type": "string"}, "start_date": {"type": "string"}, "end_date": {"type": "string"} } } }, "work_experience": { "type": "array", "items": { "type": "object", "properties": { "company": {"type": "string"}, "title": {"type": "string"}, "start_date": {"type": "string"}, "end_date": {"type": "string"}, "description": {"type": "string"} } } }, "summary": {"type": "string"} }, "required": ["name", "skills"] }

required字段表示模型输出必须包含nameskills,其他字段可为空。这样既能约束关键信息,又不会因为信息缺失导致整次生成失败。

6.3 大模型调用模块 llm_client.py

这一部分是核心。需要处理两种场景:使用 JSON 模式和不使用 JSON 模式。代码如下:

# llm_client.py import json import requests from config import LLM_BASE_URL, LLM_API_KEY, LLM_MODEL, LLM_JSON_MODE from models import RESUME_SCHEMA def build_prompt(user_input: str) -> str: """根据用户输入构造 Prompt。""" schema_str = json.dumps(RESUME_SCHEMA, ensure_ascii=False, indent=2) prompt = f""" 你是一个简历信息提取助手。请根据用户提供的原始简历描述,提取结构化信息。 具体要求: 1. 只输出一个 JSON 对象,不要输出任何解释、问候或 Markdown 代码块标记。 2. JSON 必须符合以下 JSON Schema: {schema_str} 字段说明: - name: 姓名 - age: 年龄(数字类型) - email: 邮箱 - phone: 电话 - skills: 技能列表,数组类型 - education: 教育经历列表,每个元素包含 school、degree、major、start_date、end_date - work_experience: 工作经历列表,每个元素包含 company、title、start_date、end_date、description - summary: 个人简介 如果原始描述中缺少某个字段,请将该字段设为 null(对象类型)或空数组(数组类型),不要编造。 用户原始描述: {user_input} """ return prompt def call_llm(user_input: str) -> str: """调用大模型接口,返回模型输出的原始文本。""" url = f"{LLM_BASE_URL}/chat/completions" headers = { "Authorization": f"Bearer {LLM_API_KEY}", "Content-Type": "application/json" } payload = { "model": LLM_MODEL, "messages": [ {"role": "system", "content": "你是一个严谨的数据提取助手,严格只输出 JSON。"}, {"role": "user", "content": build_prompt(user_input)} ], "temperature": 0.2 } # 如果服务支持 JSON 模式,则开启。 # 注意:不同服务对 response_format 的兼容性不同,需按实际服务调整。 if LLM_JSON_MODE: payload["response_format"] = {"type": "json_object"} resp = requests.post(url, headers=headers, json=payload, timeout=60) resp.raise_for_status() data = resp.json() return data["choices"][0]["message"]["content"]

关于temperature:数值越小,模型输出越稳定,更适合提取型任务。这里设置为 0.2,不会过分随机,也不会呆板到失去概括能力。

6.4 解析校验模块 parser.py

在实际项目中,大模型返回的结果不一定“干干净净”,所以解析和校验这一层不能省略。代码如下:

# parser.py import json import re from typing import Dict, Any from jsonschema import validate, ValidationError from models import RESUME_SCHEMA def extract_json(text: str) -> Dict[str, Any]: """从模型输出中提取 JSON 对象。""" text = text.strip() # 去掉常见的 Markdown 代码块标记 text = re.sub(r"^```(?:json)?\s*|\s*```$", "", text).strip() # 找到第一个 { 和最后一个 } start = text.find("{") end = text.rfind("}") if start == -1 or end == -1 or end < start: raise ValueError("模型输出中未找到 JSON 对象") json_str = text[start:end + 1] # 尝试直接解析 try: return json.loads(json_str) except json.JSONDecodeError: # 如果解析失败,尝试修复尾逗号 fixed = re.sub(r",\s*([}\]])", r"\1", json_str) return json.loads(fixed) def validate_resume(data: Dict[str, Any]) -> Dict[str, Any]: """校验 JSON 是否符合简历 Schema。""" try: validate(instance=data, schema=RESUME_SCHEMA) except ValidationError as e: raise ValueError(f"简历 JSON 校验失败: {e.message}") return data

6.5 文案渲染模块 renderer.py

有了结构化数据,我们可以灵活生成各种格式的文案。这里实现一个将简历 JSON 转为 Markdown 文档的函数:

# renderer.py from typing import Dict, Any def render_markdown(resume: Dict[str, Any]) -> str: """将简历 JSON 渲染为 Markdown 文案。""" lines = [] # 基本信息 name = resume.get("name", "未知") lines.append(f"# {name} 的简历") lines.append("") # 联系方式 contact_parts = [] if resume.get("email"): contact_parts.append(f"邮箱:{resume['email']}") if resume.get("phone"): contact_parts.append(f"电话:{resume['phone']}") if contact_parts: lines.append("## 联系方式") lines.append("") for part in contact_parts: lines.append(f"- {part}") lines.append("") # 个人简介 if resume.get("summary"): lines.append("## 个人简介") lines.append("") lines.append(resume["summary"]) lines.append("") # 技能 skills = resume.get("skills", []) if skills: lines.append("## 技能") lines.append("") lines.append("、".join(skills)) lines.append("") # 工作经历 work_experience = resume.get("work_experience", []) if work_experience: lines.append("## 工作经历") lines.append("") for exp in work_experience: company = exp.get("company", "未知公司") title = exp.get("title", "未知职位") start = exp.get("start_date", "") end = exp.get("end_date", "至今") date_str = f"{start} ~ {end}" if start else "" lines.append(f"### {title} @ {company} ({date_str})") if exp.get("description"): lines.append("") lines.append(exp["description"]) lines.append("") # 教育经历 education = resume.get("education", []) if education: lines.append("## 教育经历") lines.append("") for edu in education: school = edu.get("school", "未知学校") degree = edu.get("degree", "") major = edu.get("major", "") start = edu.get("start_date", "") end = edu.get("end_date", "") date_str = f"{start} ~ {end}" if start else "" lines.append(f"- {school},{degree},{major}({date_str})") lines.append("") return "\n".join(lines)

这种写法把“数据”和“展示”彻底分开,后续你想改成 HTML、PDF 或 DOCX,只需要新增一个渲染函数即可。

6.6 主流程 main.py

最后把各个模块串起来:

# main.py import json import os from config import INPUT_DIR, OUTPUT_DIR from llm_client import call_llm from parser import extract_json, validate_resume from renderer import render_markdown def load_input(filename: str) -> str: """加载用户输入的简历描述文本。""" path = os.path.join(INPUT_DIR, filename) with open(path, "r", encoding="utf-8") as f: return f.read().strip() def save_json(data: dict, filename: str): """保存 JSON 到输出目录。""" os.makedirs(OUTPUT_DIR, exist_ok=True) path = os.path.join(OUTPUT_DIR, filename) with open(path, "w", encoding="utf-8") as f: json.dump(data, f, ensure_ascii=False, indent=2) print(f"[INFO] JSON 已保存: {path}") def save_markdown(text: str, filename: str): """保存 Markdown 文案到输出目录。""" os.makedirs(OUTPUT_DIR, exist_ok=True) path = os.path.join(OUTPUT_DIR, filename) with open(path, "w", encoding="utf-8") as f: f.write(text) print(f"[INFO] Markdown 已保存: {path}") def main(): # 1. 读取原始简历描述 user_input = load_input("input.txt") print("[INFO] 原始输入已读取。") # 2. 调用大模型,得到原始响应文本 raw_output = call_llm(user_input) print("[INFO] 大模型调用完成。") # 3. 提取并校验 JSON resume_data = extract_json(raw_output) validate_resume(resume_data) print("[INFO] JSON 解析与校验通过。") # 4. 保存结构化 JSON save_json(resume_data, "resume.json") # 5. 渲染 Markdown 文案 markdown_text = render_markdown(resume_data) save_markdown(markdown_text, "resume.md") print("[INFO] 流程结束。") if __name__ == "__main__": main()

6.7 准备输入数据

data目录下创建input.txt,内容写一段模拟的简历描述:

张三,26岁,邮箱 zhangsan@example.com,电话 13800138000。 2019年本科毕业于北京理工大学计算机科学专业。 2020年加入某科技公司担任后端开发工程师,负责订单系统开发,使用 Java 和 Spring Boot,2021年晋升为高级工程师。 技能包括 Java、Python、SQL、Docker。 个人简介:热爱技术,有良好的团队协作能力,熟悉高并发系统设计与开发。

6.8 运行项目

在项目根目录执行:

python main.py

如果一切正常,你会看到类似输出:

[INFO] 原始输入已读取。 [INFO] 大模型调用完成。 [INFO] JSON 解析与校验通过。 [INFO] JSON 已保存: output/resume.json [INFO] Markdown 已保存: output/resume.md [INFO] 流程结束。

打开output/resume.json,你会看到一份标准结构化简历。打开output/resume.md,会看到渲染好的 Markdown 文案,可以直接复制到简历网站或文档里。

7. 常见问题与排查思路

在项目落地过程中,你可能会遇到下面这些问题。这里整理成表格,方便快速定位。

问题现象常见原因解决方案
模型返回内容包含解释性文字未启用 JSON 模式,或 Prompt 约束不够在 Prompt 中明确“只输出 JSON”,或使用response_format
JSON 解析报错JSONDecodeError模型输出了 Markdown 代码块、尾逗号或单引号extract_json()做后处理,必要时修复尾逗号
校验失败,缺少必填字段原始输入信息不足,模型没有推填充扩展现有字段为可选,或对缺失字段做二次追问
年龄被识别为字符串模型对类型约束不敏感在 Prompt 中强调字段类型,并用代码强制类型转换
接口返回 401/403API Key 错误或缺少权限检查环境变量配置、API Key 和套餐权限
接口返回 404LLM_BASE_URL路径不对确认接口地址是否以/v1结尾,并检查模型名是否存在
速度太慢模型较大、网络延迟或输入过长换更小模型,减少 Prompt 中不必要的示例,增加超时时间

如果你遇到“没有 API Key”的问题,可以先用本地模拟数据替代:写一个假的call_llm函数,返回你手工构造的 JSON 字符串。这样整个流程依然可以跑通,等接上真实模型时再替换调用模块。

8. 最佳实践与工程建议

8.1 Prompt 设计

  • 明确“只输出 JSON”的指令,放在 Prompt 开头,避免模型遗忘。
  • 给出 JSON Schema,让模型知道每个字段的类型和含义。
  • 对缺失字段给出处理策略,例如:“缺失字段设为 null 或空数组,不要编造”。
  • 控制temperature在 0 到 0.3 之间,提升生成稳定性。

8.2 数据校验

无论模型服务多成熟,都不能跳过校验层。建议在代码中引入 JSON Schema 校验,并在生产环境增加异常重试机制:

MAX_RETRIES = 3 for attempt in range(MAX_RETRIES): try: raw_output = call_llm(user_input) resume_data = extract_json(raw_output) validate_resume(resume_data) break except Exception as e: print(f"[WARN] 第 {attempt + 1} 次尝试失败: {e}") if attempt == MAX_RETRIES - 1: raise

8.3 日志与可观测性

记录每次调用的输入 Prompt、原始输出、解析结果和耗时。这不仅能帮你排查问题,还能为后续 Prompt 调优提供数据支撑。建议使用结构化的日志格式,例如:

{ "event": "llm_call", "model": "gpt-4o-mini", "status": "success", "latency_ms": 1200, "input_length": 320, "output_length": 280 }

8.4 安全边界

  • 不要在 Prompt 中拼接未经验证的用户输入,防止提示注入。
  • 对模型输出做字段白名单校验,只允许 Schema 中声明的字段。
  • API Key 一律通过环境变量或密钥管理服务读取,不要提交到代码仓库。
  • 涉及生产数据时,遵循最小权限原则,模型服务只需读取本次任务所需数据。

8.5 性能优化

  • 限制输入长度,避免把超长文档全部塞进 Prompt,必要时先做摘要。
  • 考虑批量请求。如果有多份简历需要处理,可以异步并行调用,但要注意 API 速率限制。
  • 对结果做缓存。相同输入不做重复调用,节省 Token 成本。

8.6 维护成本

结构化输出不是“一次写对,永远能用”。随着业务变化,你需要调整字段结构、补充枚举约束、更新 Prompt。建议把 JSON Schema 独立成版本化文件,用 Git 管理变更,必要时记录字段兼容性说明。

9. 总结与下一步学习路线

通过这篇文章,我们完成了一条完整的学习路径。先理解了为什么需要结构化输出,再快速掌握了 JSON 的基础语法,然后深入分析了大模型产生杂乱输出的原因,最后用“简历助手”项目串起了从 Prompt 构造、模型调用、JSON 解析校验到文案渲染的全流程。你现在应该已经掌握了几个关键能力:

  • 用 JSON 约束大模型输出,让程序能稳定消费模型返回的数据。
  • 用 JSON Schema 定义业务数据结构,并对模型输出做自动化校验。
  • 写一个可以复用的“文本到结构化数据”的工程项目骨架。
  • 针对常见解析失败、字段缺失、接口报错等问题做排查和修复。

下一步,你可以继续扩展这个简历助手,比如增加多轮对话,追问用户缺失的信息;接入前端页面,做成前后端分离的完整应用;或者把“简历提取”的思路迁移到“招聘信息抽取”“合同关键字段提取”等场景。结构化输出能力是 LLM 应用开发的基本功,掌握它之后,你会发现大模型集成业务系统的效率有了质的提升。

如果这篇教程对你有帮助,可以收藏备用,也欢迎在实际动手过程中记录自己的踩坑经验,形成你自己的实战笔记。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/3 7:56:40

SpringBoot考务管理系统:从架构设计到高并发实战

简介&#xff1a;本资源是一个基于SpringBoot开发的考务管理系统完整工程包&#xff0c;面向Java后端开发者及高校教育信息化项目实践者&#xff0c;旨在解决学校考试全流程数字化管理难题&#xff0c;涵盖考生、考场、试题、成绩与权限等核心业务场景。压缩包共174个文件&…

作者头像 李华
网站建设 2026/9/3 7:55:16

SpringBoot集成海康SDK实现交通违章报警与图片上传的完整实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/3 7:53:34

STM32温湿度报警系统:DHT11与DS18B20传感器驱动与项目实战

简介&#xff1a;这是一套基于STM32F1系列单片机的嵌入式温湿度采集与报警系统完整软件工程&#xff0c;面向嵌入式初学者、课程设计学生及电子竞赛备赛者&#xff0c;解决多传感器协同采集、数据转换、阈值报警与LCD界面显示等典型实践问题。资源包共203个文件&#xff0c;涵盖…

作者头像 李华
网站建设 2026/9/3 7:49:45

main_window.py(五):项目周期管理页面|信息化项目全流程管理系统源码逐行精讲(三十)

main_window.py(五):项目周期管理页面|信息化项目全流程管理系统源码逐行精讲(三十) 摘要:本文逐行精讲 main_window.py 中 UI 最复杂的项目周期管理页面。核心内容包括:13 列表格的构建与 QSS 样式、CycleItemDelegate 自定义委托的三态绘制与自适应对齐、基于 seq_me…

作者头像 李华
网站建设 2026/9/3 7:49:11

Python批量Web存活探测与标题提取工具:从并发请求到编码处理实战

简介&#xff1a;WebBatchRequest是一款面向网络技术初学者与个人学习者的轻量级批量探测工具&#xff0c;用于高效检测大批量网站地址的存活状态并提取HTML页面标题&#xff0c;适用于网站运维监控、开发环境验证及网络安全基础实践等场景。资源包共12个文件&#xff0c;含6个…

作者头像 李华
网站建设 2026/9/3 7:46:49

GRE over GRE特殊配置组网实现

一 组网说明与用户需求 如上图: 总部与分支1、分支2通过gre互通,但是总部与分支2互通需借助总部与分支1的通道,并且只需要总部与分支互通,分支与分支不能互通; 总部与分支1通过gre tunnel1互联互通,隧道源和目的地址为总部与分支1互联地址; 总部与分支2通过gre tunn…

作者头像 李华