如何从语言模型稳定拿到 JSON
要求它给 JSON,不等于拿到的就是 JSON。差距主要在结构上。
「请只返回 JSON」是一句祈使句,不是一种保证。模型会照做,然后加上一句「好的,这是你要的结果」——从语法上说,这句客套就已经让整个回复不再是合法 JSON 了。理解这件事之后,取数逻辑的写法就只有一个方向:不要试图让回复变干净,而是从中找出干净的那一段。
回复为什么总是被包起来
模型被训练成「有帮助的助手」,而助手会解释自己在做什么。于是即使你明确要求只输出 JSON,它也会倾向于加一句开场白或一段总结。这不是指令遵循失败,而是两个目标在互相拉扯。
另外两个常见包装来自格式化层:Markdown 代码围栏,以及为了可读性插入的换行与缩进。前者是展示习惯,后者纯粹是排版。
两种失败的解析写法
大多数取数代码从两个方向踩坑。
第一种是「直接解析」:把回复 trim 一下交给 JSON 解析器。模型一旦加了开场白,这条路立刻断掉,而且报的错通常很难看出真正原因。
第二种是「逐字符删开头」:用一个 try/catch 包住解析循环,从左往右删字符直到成功。它能跑通大多数情况,直到某天返回了一段被截断但恰好合法的前缀 —— 于是错误的数据静默通过,比直接失败更难查。
按结构扫描,而不是按格式处理
更稳的做法是忘掉格式,直接看括号结构:
- 从左往右找到第一个
{或[,那里是候选起点。 - 开始计数嵌套深度,遇到对应的闭合符号递减。
- 跳过字符串字面量内部的符号 —— 否则文本里的花括号会把深度计数带偏。
- 处理转义序列,让
\"不会提前结束字符串。 - 深度回到零时,这段区间就是第一个配平的 JSON 值。
这个算法不关心围栏、不关心前后说了什么,也不在乎缩进。JSON 提取器跑的就是这套扫描。
提取,但不要修复
需要明确划一条线:提取器只负责取,不负责修。少一个逗号就还是少一个逗号,缺一个引号也照样不合法。
这条线的价值在于失败是显式的。一个会「顺手修一下」的工具,最危险的输出不是报错,而是悄悄补出一个你没打算要的字段。取不到就报取不到,调用方才有机会做出正确决定。
继续阅读
- 清洗模型输出,且不丢语义
四个动作覆盖几乎所有的粘贴残留。
- 为什么 AI 工具应该跑在你的浏览器里
为什么抽取应该留在浏览器里。
- JSON 提取器
从混乱的回复里取出第一段合法 JSON。
结构化输出的可靠性不来自更强的提示词,而来自取数端的容错。把「找到第一段配平的值」这件事写进解析层,比在提示词里反复强调格式更稳。