大模型工具调用输出的 JSON,凭什么能保证不出错?

前几天一个学 Agent 的朋友在群里问了我一个问题:

Tool Call 似乎是 Agent 循环的灵魂,如果 JSON 总返回不太对的话,那当前这么繁荣的 Agent 生态不可能存在呀。所以不知道什么时候开始这个事情就被解决了,是 Function Calling 时代就解决了吗?

说实话,这个问题问得特别好。因为它直接触及了 Agent 能跑起来的一个底层前提------模型输出的 JSON 必须是合法的

你想想,一个 Agent 跑一个任务,可能要调用几十次工具。每次调用都是一段 JSON。如果有 5% 的概率格式出错,跑 20 轮就几乎必崩一次。这种东西要是不靠谱,整个 Agent 生态确实不可能存在。

那大模型到底是怎么保证 JSON 输出的正确性的?这个问题其实涉及到模型最底层的生成机制。今天就来好好聊聊。

先搞清楚一件事:模型是一个字一个字蹦出来的

很多人对大模型有个误解,觉得它是"想好了整段话再说出来的"。

不是的。大模型生成文本的过程,是一个 token 一个 token 往外蹦的。它先生成第 1 个 token,然后基于第 1 个 token 再生成第 2 个,再基于前 2 个生成第 3 个......一直到输出一个结束标记。

这个过程叫自回归生成

你在 ChatGPT 或者 Claude 里面看到的那个"打字机效果"------文字一个一个蹦出来,这不是什么动画特效,这就是模型真实的生成过程。

那问题来了:当模型要输出一段 Function Call 的 JSON 时,它也是一个 token 一个 token 蹦出来的。先蹦一个 {,再蹦一个 ",再蹦 name......

你收到的不是一个完整的 JSON,而是一堆碎片:

javascript 复制代码
{"na        ← 第一批 token
me": "get   ← 第二批
_weather",  ← 第三批
"input":    ← 第四批
{"city":    ← 第五批
"北京"}}    ← 最后一批,JSON 才完整

得等最后一个 } 蹦出来了,你才能解析这段 JSON,才能去执行工具。

好,记住这个过程,接下来是重点。

每次蹦一个 token,本质上是在"抽签"

模型每生成一个 token,内部实际发生的事情是这样的:

模型的词汇表里有几万甚至十几万个 token。每到要生成下一个 token 的时候,模型会给词汇表里的每一个 token 打一个分(叫 logits),表示"根据前面的上下文,这个 token 接下来出现的可能性有多大"。

然后这些分数经过一个叫 softmax 的函数,变成概率分布------所有候选 token 的概率加起来等于 1,我们把这个叫归一化过程。

最后从这个概率分布里"抽签",选出下一个 token。

概率高的更容易被选中,但不是 100% 确定。这也是为什么你问模型同一个问题,有时候得到不同的回答。

这个"打分 → 概率 → 抽签"的过程,就是理解后面所有事情的关键。

光靠训练,JSON 正确率只有 93%

现在我们知道了模型是怎么生成 token 的,那它输出 JSON 的时候,凭什么能保证格式正确?

第一层保障是训练

模型在训练阶段看过海量的 JSON 数据,也看过大量"给定一个 JSON Schema,输出符合 Schema 的 JSON"的样本。所以它确实学会了 JSON 的语法规则------什么时候该输出引号,什么时候该输出逗号,什么时候该闭合花括号。

但光靠训练够吗?

OpenAI 公布过一个数据:**单靠训练,JSON Schema 的符合率只有百分之 93

93% 听起来挺高的?但你换算一下:100 次工具调用里有 7 次格式出错。一个 Agent 跑一个稍微复杂点的任务,10 轮工具调用就大概率崩一次。

在生产环境里,93% 远远不够。

那怎么办?

约束解码:把不合法的选项提前排除掉

这就是约束解码(Constrained Decoding) 登场的时候了。

还记得刚才说的吗?模型每次生成 token 的时候,会给所有候选 token 打分,然后从概率分布里抽签。

约束解码做的事情非常直觉------在抽签之前,把不合法的选项直接排除掉

具体是这样的:

  1. 先把 JSON Schema 编译成一套语法规则
  2. 每生成一个 token,检查:根据已经生成的内容,接下来哪些 token 在语法上是合法的
  3. 不合法的 token,概率直接设为 0
  4. 剩下的合法 token 重新归一化(Softmax),正常抽签

举个例子。假设 Schema 要求有个 city 字段,类型是 string。当模型已经生成到 "city": 的时候,下一个 token 按照 JSON 语法只能是 "(字符串的开始引号)。数字、布尔值、null,这些 token 的概率全被设为 0 了。模型想选也选不了。

再比如,模型已经输出了 {"name": "get_weather", "input": {"city": "北京",这时候下一个 token 只能是 } 或者 ,(如果还有其他字段的话)。绝对不可能蹦出一个不合法的字符。

训练让模型"想"输出正确的 JSON,约束解码让模型"只能"输出正确的 JSON。

两者加在一起,JSON 格式的正确率就是 100% 了。

这就是为什么现在 Agent 生态能繁荣起来。Function Calling 在底层做了约束解码,从机制上保证了格式不会出错,而不是靠"模型比较聪明所以一般不会错"这种概率性的东西。

但约束解码只管格式,不管语义

这里要补一个很重要的点。

约束解码能保证的是格式上的正确性------JSON 语法一定是合法的,字段类型一定是对的。

但它不能保证语义上的正确性。

什么意思?比如你的工具有个参数 city,类型是 string。约束解码能保证模型输出的确实是一个字符串,但它不能保证这个字符串是一个真实存在的城市。模型完全可以填个"哥谭市"进去------格式完全正确,JSON 解析没问题,但执行必然失败。

再比如,模型可能编一个看起来像 UUID 但完全不存在的 ID,可能拼一个看起来合理但并不存在的文件路径。这些都是格式正确但语义错误的情况。

所以在实际的 Agent 系统里,光有约束解码还不够,你还需要参数校验、执行前检查、以及清晰的错误反馈。这些是另外一个话题了。

那 System Prompt 约束 JSON 输出靠谱吗?

那个朋友还追问了一个问题:如果用 OpenSpec 那种基于 system prompt 的 CLI 系统,约束相对 Tool Call 会弱很多吧?

确实会弱很多。

System prompt 里写"请用 JSON 格式输出",这种约束本质上是自然语言层面的引导。模型会"尽力"按你说的格式输出,但它没有任何机制层面的保障。模型在生成过程中,每一步选 token 的时候,所有 token 都是可选的------包括那些会破坏 JSON 格式的 token。

而 Function Calling / Tool Call 的约束解码是机制层面的保障。在 token 抽签之前,不合法的选项已经被排除了。模型不是"尽力"不犯错,而是"不可能"犯错。

举个形象的例子,这就像一个是告诉司机"请走右边车道",另一个是直接在路中间立了隔离带。前者靠自觉,后者直接靠物理约束,效果立竿见影。

所以如果你在做 Agent 开发,涉及到工具调用的场景,一定要用模型原生的 Function Calling 能力,而不是用 system prompt 来约束输出格式。这不是"好不好用"的问题,是"能不能在生产环境跑"的问题。

再往深想一层

其实理解了约束解码,你会发现它的应用不止 Function Calling。

比如结构化输出(Structured Output)------你想让模型按固定格式输出一个评分结果、一段摘要、一组提取的实体,都可以用同样的技术。给一个 JSON Schema,模型的输出就被限制在这个 Schema 的语法范围内。

再比如 Manus 用的 response prefill 技巧。既然模型是基于前面所有 token 来决定下一个 token 的,那你提前替模型把前几个 token 写好,模型就只能顺着你的开头继续往下生成。比如你在 assistant 消息里预填 {"name": "browser_,模型就只能在 browser_ 开头的工具名里选了。这也是一种约束,只不过是另外一种思路。

这些技巧背后的原理是一脉相承的:理解了模型"一个 token 一个 token 抽签"的生成机制,你就能理解所有这些控制手段为什么有效。

最后

回到开头那个问题------Tool Call 的 JSON 输出凭什么能保证正确?

答案就三句话:

  1. 模型是一个 token 一个 token 生成的,每次都在概率分布里抽签
  2. 约束解码在每次抽签前把不合法的选项排除掉
  3. 训练 + 约束解码 = 100% 格式正确

这不是模型"聪明",这是机制设计。

其实这个问题之所以值得拿出来聊,是因为它触及到了 Agent 开发里一个非常底层但又非常关键的知识点。做 Agent 不是简单地调调 API、写写 prompt 就完了,底层这些机制你不理解,很多决策你做不对,你会很困惑。相信这个朋友的疑惑并不是个例,其实非常普遍。

最近也确实有很多读者朋友找我来学 Agent。我最近准备开一门「吃透 Agent 知识体系」以及相关实战的课,即将开始预售。我们从大模型底层的原理讲起,一直到 Agent Loop、Tool Call 系统设计、Context Engineering 进阶等六大板块,彻底学通 Agent 开发领域,并且还有硬核的实战项目。

想参与早鸟特惠的,可以加我微信 sanyuan0704,了解更多信息,备注"agent 早鸟学习"即可。

相关推荐
是烟花哈4 小时前
【前端】React框架学习
前端·学习·react.js
qq4356947015 小时前
JavaWeb08
前端
甲维斯5 小时前
DeepSeek V4 Flash 好东西啊,多快好省!
ai编程·deepseek
冬奇Lab5 小时前
Claude Code 接入 SonarQube 静态扫描:AI 写代码,质量闭环了
人工智能·ai编程·claude
2401_878454536 小时前
html和css的复习(1)
前端·css·html
@PHARAOH6 小时前
WHAT - git worktree 概念
前端·git
IT_陈寒7 小时前
我竟然被JavaScript的隐式类型转换坑了三天!
前端·人工智能·后端
XinZong7 小时前
【AI社交Skill】禁止人类发言”的AI闭环社交社区_ 到底什么是 clawreach 虾聊?
aigc·openai·ai编程
孟健7 小时前
程序员创业半年:顺的事、不顺的事,和我一直没想清楚的事
ai编程
我亚索贼六丶7 小时前
二十六. AI基础概念之如何更好的使用AI
前端