Prompt 工程实战:让模型稳定输出结构化 JSON

Words: 1309Read Time: 4 minLast edited: 2026-8-4

问题:模型的嘴,骗人的鬼

事情起源于公众号后台。我维护的公众号允许用户留言发快递收件信息,格式千奇百怪:"帮我寄北京市海淀区 xxx,王五 139xxxxxxxx"、"电话 135xxx,地址广东深圳南山区,收件人李四"。人工抄录太累,我想让大模型自动把姓名、电话、地址提取成结构化数据,直接存进数据库。
理想很丰满。第一次试,模型的输出长这样:
好的,根据您提供的留言,提取结果如下:{"name": "王五", "phone": "139xxxxxxxx", "address": "北京市海淀区 xxx"}。请问还有什么可以帮您?
它有时还会在 JSON 外面包一层 markdown 代码块,前后附赠两句客套话。程序拿去 json.loads,直接炸给你看。这就是让模型输出结构化数据的经典难题:它是一个话痨,而你需要的是一个沉默的解析器

技巧一:指令写清楚,字段定死

先说结论:提需求的方式决定输出的稳定性。几个要点:
  • 明确说"只输出 JSON,不要输出任何解释,不要使用 markdown 代码块"。
  • 字段名、字段类型定死,并说明提取不到怎么办(填 null),别让模型自由发挥。
  • 在 prompt 里给出 JSON 的示例结构,模型对格式的模仿能力很强。
"你是一个信息提取助手"这种角色设定也有用,相当于先把它框进一个工作模式里。

技巧二:给两个 few-shot 示例

光说规则不如给例子。在 prompt 里放 1~2 组完整的"输入→输出"示例(few-shot),稳定性会有质的提升——示例里没有废话,它就大概率不说废话。我的完整 prompt 模板:
注意示例二我故意放了一个缺电话的留言,教模型遇到缺字段时输出 null,而不是瞎编。不教它的话,它真的会给你编一个 138 开头的手机号——这种"一本正经的幻觉"比报错可怕得多,因为数据悄悄就脏了。

技巧三:代码兜底,别全信模型

就算 prompt 写得再好,模型也只是"大概率"听话。跑了上千次之后,总有几次它会多说话、把字段名写成别的、或者给出不合法的 JSON(比如结尾多一个逗号)。所以代码层必须有兜底:
思路是先把输入当纯 JSON 解析;失败就用正则把第一个 {...} 块抠出来再试;再失败就抛异常,外层重试一次(重试时把 temperature 调低效果更好)。加了这层兜底后,解析成功率从 97% 左右提到接近 100%。
另外补一句:2023 年底之后的新模型支持 response_format={"type": "json_object"} 这个参数,也就是 JSON mode,开启后 API 层面保证输出是合法 JSON。但它有两个限制:prompt 里必须包含 "JSON" 字样,否则直接报错;而且它只保证"合法 JSON",不保证字段符合你的 schema——该做的校验一个都不能少。

实战体会

这套"指令 + few-shot + 兜底解析"的组合拳打下来,留言提取的准确率稳定在可用水平,人工只需要抽查。回头看核心心法就一句:把模型当成能力很强但不太守规矩的实习生——规矩要立,示例要给,产出要验收,三者缺一不可。
Loading...
© 2024 - 2026 ihuadz
中文