游乐游手机版
首页/AI教程/文章详情

大模型工具调用JSON输出如何保证不出错

时间:2026-05-28 19:01
前几天一个学 Agent 的朋友在群里问了个问题: 说实话,这问题问得真妙——它直接戳中了 Agent 能跑起来的底层前提:模型输出的 JSON 必须是合法的。 你想啊,一个 Agent 跑一个任务,可能要调用几十次工具。每次调用都是一段 JSON,如果 5% 的概率格式出错,跑 20 轮就几乎必崩

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

说实话,这问题问得真妙——它直接戳中了 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,而是一堆碎片:

{"na← 第一批 tokenme": "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 就完了,底层这些机制你不理解,很多决策你做不对,你会很困惑。相信这个朋友的疑惑并不是个例,其实非常普遍。

希望这篇文章能帮你把这块彻底理清楚。

来源:https://juejin.cn/post/7625237255016988691
上一篇WPS一键生成PPT,提升工作效率与专业感 下一篇两步快速搞定AI生成民间故事视频教程
本站内容用于信息整理与展示,如有侵权或内容问题请及时联系处理。

相关推荐

补充同频道和同主题内容,方便继续浏览更多相关内容。

同类最新

继续查看同栏目最近更新的文章。

更多
OpenClaw浏览器自动化控制 Playwright MCP与Mcporter方案实现完整流程步骤详解教程
AI教程 · 2026-06-01

OpenClaw浏览器自动化控制 Playwright MCP与Mcporter方案实现完整流程步骤详解教程

概述 这篇文章记录了把Playwright MCP集成到OpenClaw中,并用Mcporter作为中间桥梁的完整测试过程。内容包括问题诊断、架构理解,以及正确的使用方法——说白了,就是带大家把整个链路彻底捋清楚。 先交代一下背景:为啥折腾这个方案?说实话,就是熬夜后闲得慌,突发奇想想在家里搞搞Op

AI写业务代码后必须坚持的过程控制
AI教程 · 2026-06-01

AI写业务代码后必须坚持的过程控制

前言AI 已经能极其高效地帮我们搞定业务代码了。这个结论经过反复验证,基本上没什么悬念。但问题也随之而来:越是这样,越容易陷入失控状态——想到哪写到哪,总盼着 AI 一口气把活儿全干了。业务代码和 demo 最大的不同在于,业务从来不是孤立的。它牵扯着一连串的业务流程、历史包袱、数据状态、权限边界、

我用两个高效技巧解决AI开发文档记录难题
AI教程 · 2026-06-01

我用两个高效技巧解决AI开发文档记录难题

我用 AI 写了三个月代码,结果连自己写的东西都看不懂了 一个开发者的普遍困境 从去年开始,大量开发者涌入 Claude Code 进行 AI 辅助开发。效率提升令人振奋——过去需要两天的功能,现在一个下午就能搞定。但很快,一个尴尬的问题浮出水面:三个月前自己写的代码,如今竟然看不懂了。 问题不在于

AI改坏真实App的常见问题与解决技巧
AI教程 · 2026-06-01

AI改坏真实App的常见问题与解决技巧

探索AI辅助移动端开发的过程中,我属于较早深入实践并持续积累经验的那一批。过去几个月里,我几乎每天都会在真实的iOS与Flutter项目中与AI协作调整代码:涵盖SDK封装、旧代码迁移、Demo补全、使用文档优化、多语言适配、界面检查、验证执行以及工作交接整理。因此,本文无意纠缠“AI究竟能否编写代

领导要求部署OpenClaw?先看这篇指南
AI教程 · 2026-06-01

领导要求部署OpenClaw?先看这篇指南

前几天,领导丢过来一句话:你去看一下 OpenClaw,评估一下能不能在公司内部部署。紧接着又问了一个很典型的问题:这东西到底算什么?是一种云服务吗? 仔细一想,这个问题的答案并不简单。OpenClaw 本身不等于“云平台”,但一旦真正用起来,云环境通常会深度参与。它更像一层编排和运行框架,负责把袋