改提示词让输出稳定JSON
让AI稳定输出JSON老出岔子,偶尔多个逗号少个引号,解析端一报错就得手动改半天。
有人这样说「刚被老板催着把数据导出成JSON,结果AI老出错,解析直接崩了。」
能替你选的都选好了。不想动,直接点下面「交给 AI 办」,点一下就开始做;想改,就点一下要的那个。
已替你选好想改就点一下
只有你知道的不填就先空着,成品里留【待填】
还能补充
还想怎么改可以不选
高级选项一般不用动 · 这件事通常 1 分钟左右做好
示例成品全文按上面这组示例选项真跑出来的
直接给成品。不假设你有严格 schema 能力,只按你说的 只有 json_mode 来。
1. 改进后的提示词模板
{{text}} 替换成你要抽取的原文。
你是数据抽取器。只输出一个 JSON 对象,不要输出任何解释、前后缀、Markdown 代码块。
输出必须能被 Python json.loads 直接解析。严格按下面结构输出:
{"name":"姓名","age":20,"skills":["技能1","技能2"]}
强制规则:
- 字段名只能是 name、age、skills,不要新增字段
- name 必须是字符串
- age 必须是整数,不要输出 "20岁"、20.0、null
- skills 必须是字符串数组,不能是对象数组
- 提取不到时:name 用 "",age 用 0,skills 用 []
- 禁止用 Markdown 的 json 代码块包裹
- 禁止在 JSON 前后加任何文字
待抽取文本:
{{text}}2. Python 可跑代码
这段代码会:
- 用
json_mode调用 GPT - 就算模型抽风混进 markdown 或解释文字,也会自动提取 JSON
- 字段尽量转成
name / age / skills,不会因为小错误直接崩
import json
import re
import warnings
from openai import OpenAI
# pip install openai --upgrade
# 需要设置 OPENAI_API_KEY 环境变量,或用 OpenAI(api_key=...)
PROMPT_TEMPLATE = """你是数据抽取器。只输出一个 JSON 对象,不要输出任何解释、前后缀、Markdown 代码块。
输出必须能被 Python json.loads 直接解析。严格按下面结构输出:
{"name":"姓名","age":20,"skills":["技能1","技能2"]}
强制规则:
- 字段名只能是 name、age、skills,不要新增字段
- name 必须是字符串
- age 必须是整数,不要输出 "20岁"、20.0、null
- skills 必须是字符串数组,不能是对象数组
- 提取不到时:name 用 "",age 用 0,skills 用 []
- 禁止用 Markdown 的 json 代码块包裹
- 禁止在 JSON 前后加任何文字
待抽取文本:
{{text}}
"""
def _extract_json(text: str):
text = text.strip()
if text.startswith("\ufeff"):
text = text[1:]
# 去掉开头/结尾的 Markdown 代码围栏
text = re.sub(r"^```(?:json)?\s*", "", text, flags=re.IGNORECASE)
text = re.sub(r"\s*```$", "", text, flags=re.IGNORECASE)
decoder = json.JSONDecoder()
idx = 0
while idx < len(text):
try:
obj, _ = decoder.raw_decode(text, idx)
return obj
except json.JSONDecodeError:
idx = text.find("{", idx)
if idx == -1:
break
idx += 1
raise ValueError(f"未找到可解析的 JSON,原文:{text!r}")
def _to_int(v, default=0):
if v is None or v == "":
return default
if isinstance(v, bool):
return default
try:
f = float(v)
if f.is_integer():
return int(f)
except (TypeError, ValueError):
pass
warnings.warn(f"age 字段无法转成整数,已默认 {default}:{v!r}")
return default
def parse_person(raw: str) -> dict:
data = _extract_json(raw)
if not isinstance(data, dict):
raise TypeError("JSON 顶层必须是对象")
name = data.get("name", "")
name = "" if name is None else str(name).strip()
age = _to_int(data.get("age"), 0)
skills = data.get("skills", [])
if skills is None:
skills = []
if isinstance(skills, str):
skills = [s.strip() for s in skills.split(",") if s.strip()]
elif isinstance(skills, list):
cleaned = []
for s in skills:
if isinstance(s, str):
cleaned.append(s.strip())
elif isinstance(s, (int, float)):
cleaned.append(str(s))
else:
warnings.warn(f"skills 里跳过非字符串项:{s!r}")
skills = cleaned
else:
warnings.warn(f"skills 字段类型异常,已默认 []:{skills!r}")
skills = []
return {"name": name, "age": age, "skills": skills}
def extract_person_from_text(text: str, client=None):
client = client or OpenAI()
resp = client.chat.completions.create(
model="gpt-4o-mini", # 想更稳可以换成 gpt-4o
response_format={"type": "json_object"},
messages=[
{
"role": "user",
"content": PROMPT_TEMPLATE.replace("{{text}}", text),
}
],
temperature=0,
)
raw = resp.choices[0].message.content
retur