让模型吐出能直接进表格的数据:LLM 结构化输出与信息抽取技术拆解
办公自动化里有个特别常见的活:从一堆乱七八糟的文本里,抽出一组规规矩矩的字段,塞进表格或数据库。合同里的甲乙方和金额、发票里的税号和价税合计、简历里的姓名和年限,干的都是同一件事——把"自然语言"变成"结构化数据"。
让大模型读文字容易,让它的输出能直接被程序当 JSON 解析、不出岔子,却是另一门功夫。这篇就拆这条"怎么让模型输出能直接用"的技术链,看从朴素的方案到更稳的方案,各自解决了什么、又留下什么。

朴素的思路:prompt 里喊一句「输出 JSON」
起步的做法几乎人人都这么干:在 prompt 末尾加一句"请以 JSON 格式输出",然后指望模型自觉遵守。
多数时候它确实会吐出一个大括号开头的字符串。但坏就坏在"多数时候"——剩下的时间,它会画蛇添足加一段"好的,这是结果:",或者在 JSON 里夹一句注释,或者字段名时大时小写,或者数值类型一会儿字符串一会儿数字。程序一 `json.loads`,直接崩。
根源在于:JSON 是一门语法很严的语言,而大模型是一路按概率吐 token 的自回归机器,它没有"语法意识",不知道某个引号、逗号、括号放错就整个非法。你提示得再恳切,也只是提高了"它想输出合法 JSON"的概率,给不了硬保证。
给模型立规矩:JSON Schema 和 Function Calling
想让输出稳定,得把"我要什么"从一句软话,变成一份可约束的规格,这就是 JSON Schema 的作用——它把字段名、类型、是否必填、可选值,都写成一纸明确的定义。
比如抽一张发票,Schema 可以规定:`invoice_no` 是字符串、`total_amount` 是数字、`invoice_type` 只能是"专票/普票"两个枚举值之一。有了这份定义,模型照着填,字段就不会乱飞,类型也不会乱变。
Function Calling(函数调用)里那个 `arguments`,本质也是模型按一份 JSON Schema 生成的参数——所以用函数调用来做抽取,天然就吃到了 Schema 的约束。很多抽取管线,就是套一个"定义函数参数 Schema、让模型填参"的壳,把结果当成结构化字段拿回来。
但 Schema 依然是"软约束":它告诉模型该长什么样,却拦不住模型在采样时吐出打破 JSON 语法的 token。Schema 解决的是"字段和类型对不对",没解决"这串东西到底是不是能解析的 JSON"。
硬保证来自哪:约束解码
要真正保证"输出一定是合法 JSON",得上另一个层级的办法——约束解码,也叫受控生成(constrained decoding)。
它的逻辑不复杂:模型每一步采样,都是在整个词表上挑下一个 token。约束解码在这一步插进来,维护一个 JSON 的语法状态机,实时判断"当前时刻,哪些 token 是允许的"。凡是会把 JSON 带进非法状态的 token,概率直接压成零,只从合法的候选里采。于是每一步都走在合法 JSON 的路径上,走到底,必然是一串能解析的 JSON。
举个直白的点:输出到一半,当前正处于"该填一个字符串值"的位置,那下一个 token 只可能是开头的引号,其他几千上万个没用的 token 全被屏蔽。就这样一路把关,语法错误从根上被掐断。
代价也有:实现复杂,不是每家服务都开;屏蔽过程会动概率分布,个别时候生成质量会略微受影响;早期有些实现还会拖慢一点速度。但对"必须能解析"的办公抽取场景,这点代价换来的确定性,通常是划算的。
别把希望全押在一处:校验 + 重试兜底
工程上把结构化输出做成生产级,几乎不会只押一种手段,而是叠三件套:约束 + 校验 + 重试。
约束负责从源头减少非法输出;校验负责把漏网的再筛一遍——拿到结果先按 Schema 验字段、类型、枚举值,不合法就进下一步;重试负责给第二次机会,把校验失败的信息连同原输入重新喂给模型,让它修正。
还有几个容易忽略的细节。一是容错解析,模型偶尔在纯 JSON 前后包一层文字,先用正则把外层的大括号抠出来再解析,能救回一大片;二是缺失值兜底,Schema 里"必填"字段万一抽不到,得有明确的空值约定,别让一条缺字段的记录拖死整批任务;三是嵌套和数组,复杂结构要提前在 Schema 里写清层级,再配合约束解码一层层保证。
小结
把结构化输出这条线收一下:一句话"输出 JSON"是软的,扛不住大模型不懂语法这件事;JSON Schema 和 Function Calling 把"要什么字段、什么类型"变成规格,管住了字段层;真正的语法保证靠约束解码,在采样每一步屏蔽非法 token;生产落地再叠上校验和重试,把漏网和缺失兜住。
下次在办公自动化里接一份发票、合同、简历的批量抽取,就知道这套"能直接进表格"的能力不是模型开箱自带,而是约束、校验、重试三层一起磨出来的。
