大模型只吐字符串,凭什么你能拿到 JSON 对象?

系列第 1 篇。你可能遇到过这个场景:让大模型"返回 JSON",它确实乖乖回了 {"name":"爱因斯坦",...}------但你在代码里 result.name 却得到 undefined。问题出在哪?

先回答一个最底层的问题:大模型输出的到底是什么?

大模型是"打字机",不是"数据库"

大模型的本质是逐 token 预测 :根据已有文本,按概率预测下一个 token,拼成完整回复。token 解码出来就是字符------也就是字符串

无论你用的是 OpenAI、DeepSeek 还是 LangChain,接口里那个 message.content 的类型永远是 string

js 复制代码
// 你以为
response.content = { name: "爱因斯坦", birth_year: 1879 }   // ❌ 不存在

// 实际
response.content = '{\n  "name": "爱因斯坦",\n  "birth_year": 1879\n}'  // ✅ 字符串

模型内存里没有"对象"这个东西 。所谓"让它返回 JSON",只是让它吐出一段"长得像 JSON 的文本"。至于这段文本是不是真的符合 JSON 语法、有没有裹 ```````json````、有没有在开头啰嗦两句------它都不保证

那为什么必须变成对象?

因为程序不读文本,程序读结构

一段字符串 '{"name":"爱因斯坦"}' 对代码来说是一个"整体":你不能 content.name,不能遍历 content.fields,不能直接塞给数据库。只有经过 JSON.parse 变成内存里的对象,字段才可访问、可迭代、可被校验、可传走。

js 复制代码
const s = '{"name":"爱因斯坦"}';
s.name;              // undefined ------ 字符串上没有属性
JSON.parse(s).name;  // "爱因斯坦" ------ 对象上才有

"对象"是你和下游代码之间唯一可消费的形态。大模型的"回答"要变成你程序的"数据",中间必然要过一道 parse。

拦路虎:模型总爱把 JSON 裹进 markdown

你光会 JSON.parse 还不够。实际调大模型时,输出经常长这样:

json 复制代码
好的,以下是爱因斯坦的信息:
```json
{"name": "爱因斯坦", "birth_year": 1879}
ini 复制代码
模型把 JSON 裹进了 markdown 代码块,还带一句开场白。直接 `JSON.parse` 会报错。于是最常见的处理是**先用正则把代码块里的 JSON 抠出来再 parse**:

```js
const jsonMatch = content.match(/```json\s*([\s\S]*?)\s*```/);
const jsonStr = jsonMatch ? jsonMatch[1] : content;  // 剥围栏,没有围栏就用原文
const result = JSON.parse(jsonStr);                  // 再 parse

这套"剥 markdown → JSON.parse"几乎是每次调大模型的固定动作------所以 LangChain 把它封装成了解析器。

JsonOutputParser:把上面的套路封装好

js 复制代码
import { JsonOutputParser } from "@langchain/core/output_parsers";

const parser = new JsonOutputParser();
const result = await parser.parse(response.content);
console.log(result.name);   // "爱因斯坦" ------ 直接可用

它内部做了什么?翻源码(@langchain/coreparseJsonMarkdown):

  1. trim 掉首尾空白;
  2. 找到第一个 ```````````,剥掉可选的 json 语言标注;
  3. 截到第二个 ``````````` 之间的内容;
  4. 交给 JSON.parse

和你手写的正则殊途同归 ,只是它更宽容:不认 json 标注的裸围栏也能剥、只有一个围栏也能处理。而你手写版只认 ```````json```` 这一种。

一个容易踩的坑:它的格式说明是空的

很多教程会让你在 prompt 里拼上 parser.getFormatInstructions()

js 复制代码
const prompt = `
请介绍爱因斯坦的信息,包含 name、birth_year 等字段。
${parser.getFormatInstructions()}
`;

但对 JsonOutputParser 来说,这一行什么都没加 ------它的 getFormatInstructions() 返回的是空字符串 。源码注释说得很直白:JSON 太常见,不需要额外教模型。所以真正约束模型输出格式的,始终是你 prompt 里自己写的那句中文。这个解析器只负责"事后把模型交上来的东西宽容地解析",不负责"事前约束"。

小结

ini 复制代码
你写 prompt(文本)
   ↓
LLM = 打字机 → 永远输出 字符串(可能裹 markdown、夹废话)
   ↓
JsonOutputParser = 剥围栏 + JSON.parse → JS 对象
   ↓
result.name ✅
  • 大模型天生只吐字符串,"JSON"只是内容长得像 JSON 的文本;
  • 变成对象是你的 parse 完成的,不是模型送的;
  • JsonOutputParser = 剥围栏 + JSON.parse 的封装,宽容但不校验。

下一篇我们会看到它的短板:它不校验字段 。模型少给你一个字段、把 birth_year 写成字符串,它都一声不吭。要"讲清楚再验收",就该轮到 StructuredOutputParser 出场了。

相关推荐
不好听6131 小时前
大模型没有记忆:Agent 为什么需要一套记忆系统
llm
AIGC大时代1 小时前
评科研 LLM/Agent:从读论文抽检到 ERA 树搜索写可计分实证软件
llm·agent·评测·科学发现·google research
不好听6131 小时前
从裸 messages 数组到 ChatMessageHistory:记忆的第一层抽象
llm
武子康2 小时前
中文 Prosody-Aware Eval:从四象限样本到可诊断的实时语音评测
人工智能·llm·agent
流浪0014 小时前
大模型技术全景(五):开源大模型生态指南,Hugging Face 与魔搭社区
人工智能·深度学习·llm
Patrick在香港17 小时前
模型路由器实测:12 个任务省 77%,旗舰过载时的降级要打出显式标记
python·llm·智能路由器·api·架构设计·成本优化
桃西西呀19 小时前
"循环深度"让小模型免费变大?GPT-6 Astra 背后的潜空间推理原理,8 个误读我替你拆了
人工智能·gpt·llm
Sophnet云平台1 天前
从 IT 自嗨到业务可用,制造企业 AI 平台的落地实践
大数据·人工智能·llm·制造·token·云平台
JouYY1 天前
大模型底层学习(四)- 混合精度训练与分布式训练
架构·llm·agent