🚀 告别解析报错!用 LangChain + Zod 打造结构化数据提取神器

48 阅读6分钟

各位掘友,大家好!我是你们的 AI 效率教练。

在使用 DeepSeek 或者 GPT 开发应用时,你是否遇到过这样的尴尬:

你:请给我输出一个关于“Promise”的 JSON。

AI:好的,没问题!这是一个关于 Promise 的 JSON 格式:json { "name": "Promise" ... } 记得给点赞哦!

焯! 那个“好的,没问题”和 Markdown 的反引号直接让 JSON.parse 报错了,有没有?在生产环境下,我们需要的不是对话,而是纯净、稳定、符合 Schema 的 API 响应

今天,我们就以一个“前端概念知识库生成器”为例,深度拆解 LangChain 中的四大金刚:ChatDeepSeekZodJsonOutputParserPromptTemplate


🛠️ 核心装备清单

在开始撸代码前,先看看我们的武器库:

  1. ChatDeepSeek: 目前国产大模型之光,性价比极高,推理能力(Reasoner)直接对标顶级模型。
  2. Zod: 前端界 Schema 校验的霸主,定义数据结构的“法律条文”。
  3. JsonOutputParser: LangChain 的“翻译官”,负责将 AI 的长篇大论修剪成规整的 JSON。
  4. PromptTemplate: 提示词模板,解决 Prompt 复用的关键。

终端运行npm i langchain @langchain/deepseek @langchain/core zod 导入上述所有依赖,pnpm一样看个人选择


🏗️ 第一步:定义规则(Zod)

在 LangChain 的工作流中,先定规矩,再写逻辑。我们希望模型生成的每一个概念都包含:名称、核心要点、使用场景和难度。

TypeScript

import { z } from 'zod';

// 使用 Zod 定义前端概念的“数字身份证”
const FrontendConceptSchema = z.object({
  name: z.string().describe('概念名称'),
  core: z.string().describe('核心要点'),
  usecase: z.array(z.string()).describe('场景使用场景'),
  difficulty: z.enum(['简单', '中等', '复杂']).describe('学习难度')
});

💡 为什么是 Zod?

Zod 不仅仅是做类型检查,它最强大的地方在于 .describe()。

这个方法描述的文字会被 JsonOutputParser 自动提取并喂给 AI。AI 看到这些描述,就知道该字段具体的含义,极大提高了生成的准确性。


🧬 第二步:初始化解析器(JsonOutputParser)

有了规矩,我们需要一个执行官来监督 AI 的输出。

javaScript

import { JsonOutputParser } from '@langchain/core/output_parsers';

// 将我们刚才定义的 Zod Schema 传给解析器
const jsonParser = new JsonOutputParser(FrontendConceptSchema);// 从z提取生成格式约束prompt

知识点解析:

JsonOutputParser 扮演了两个角色:

  1. 指导者:它提供了一个 getFormatInstructions() 方法,能自动生成一段“严厉”的提示词,告诉 AI 必须输出什么样的 JSON。
  2. 质检员:当模型返回字符串时,它会自动运行校验逻辑。如果格式不对,它会直接抛出错误或尝试修复。

🎨 第三步:构建动态 Prompt 模板

写 Prompt 最忌讳硬编码。我们要写一个万能模板,让它可以动态生成任何概念的词条。

javaScript

import { PromptTemplate } from '@langchain/core/prompts';

const prompt = PromptTemplate.fromTemplate(`
  你是一个专业的前端技术专家,只会输出 JSON 的 API,不允许输出任何解释性文字。

  ⚠️ 你必须【只返回】符合以下 Schema 的 JSON:
  - 不允许增加字段
  - 不允许减少字段
  - 字段名必须完全一致
  - 返回结果必须可以被 JSON.parse 成功解析

  {format_instructions}

  你要解析的前端概念是:{topic}
`);

🧠 深度思考:{format_instructions} 是什么?

当你调用 jsonParser.getFormatInstructions() 时,它会生成类似下面这段话:

"The output should be formatted as a JSON instance that conforms to the JSON schema below. As an example, for the schema..."

“输出应格式化为符合以下 JSON Schema 的 JSON 实例。例如,对于该 Schema……”

这就是 LangChain 的迷人之处:自动化。 你不需要手动手写那冗长的 JSON 定义。


🤖 第四步:召唤 DeepSeek 推理引擎

DeepSeek 的 deepseek-reasoner(R1 模型)在逻辑推理上非常出色,非常适合处理结构化数据的提取。

javaScript

import { ChatDeepSeek } from '@langchain/deepseek';
import 'dotenv/config';

const model = new ChatDeepSeek({
  model: 'deepseek-reasoner',
  temperature: 0, // 设为 0,保证输出的稳定性,不要让 AI 太“浪”
});

注意: 对于解析 JSON 的任务,temperature 务必设为 0。我们要的是确定性,而不是诗情画意。


🔗 第五步:组装 LCEL 链(终极合体)

LangChain 最引以为傲的就是其 LCEL (LangChain Expression Language) 。我们可以像搭积木一样用 .pipe() 将各个组件连接起来。

javaScript

// 1. 提示词编译 -> 2. 模型调用 -> 3. JSON 解析器验证
const chain = prompt.pipe(model).pipe(jsonParser);

// 执行工作流
const response = await chain.invoke({
  topic: 'Promise',
  format_instructions: jsonParser.getFormatInstructions(),
});

console.log('✨ 最终解析到的结构化数据:');
console.log(response);

🔄 工作流原理解析

  1. 输入阶段:我们将 topicformat_instructions 注入模板。
  2. 模型阶段ChatDeepSeek 接收到 Prompt。由于 Prompt 里明确要求“只输出 JSON”,加上 DeepSeek 强大的指令遵循能力,它会返回一个 JSON 字符串。
  3. 解析阶段JsonOutputParser 拿到字符串,去掉可能存在的 Markdown 标签(如 ```json),然后用 Zod 进行校验。
  4. 输出阶段:你拿到的不再是字符串,而是一个可以直接调用的 JavaScript 对象

完整代码

javaScript

import {ChatDeepSeek} from '@langchain/deepseek'
import {PromptTemplate} from '@langchain/core/prompts'
import {JsonOutputParser} from '@langchain/core/output_parsers'// 引入 json 解析器
import {z} from 'zod'
import 'dotenv/config'

const model = new ChatDeepSeek({
  model: 'deepseek-reasoner',
  temperature: 0,
});
// 前端概念 zod 校验 对象
const FrontendConceptSchema = z.object({
  name: z.string().describe('概念名称'),
  core: z.string().describe('核心要点'),
  usecase: z.array(z.string()).describe('场景使用场景'),
  difficulty: z.enum(['简单', '中等', '复杂']).describe('学习难度')
});
// console.log(FrontendConceptSchema);

const jsonParser = new JsonOutputParser(FrontendConceptSchema);
const prompt = PromptTemplate.fromTemplate(`
  你是一个只会输出 JSON 的 API,不允许输出任何解释性文字。

  ⚠️ 你必须【只返回】符合以下 Schema 的 JSON:
  - 不允许增加字段
  - 不允许减少字段
  - 字段名必须完全一致,使用name、core、useCase、difficulty
  - 返回结果必须可以被 JSON.parse 成功解析

  {format_instructions} // 类似于schema的描述告诉ai输出的json格式

  前端概念:{topic}
`);
// chain 提示词编译->模型调用->json解析器验证
const chain = prompt.pipe(model).pipe(jsonParser);
console.log(jsonParser.getFormatInstructions(),'zod');
const response = await chain.invoke({
    topic:'promise',
    format_instructions: jsonParser.getFormatInstructions(),// 在这里传入一段严格的格式约束prompt
})
console.log(response);

🛠️ 实战避坑指南

1. 字段名大小写敏感

注意 Zod 中的 usecase 和 Prompt 里如果手写了 useCase,可能会导致校验失败。建议始终以 Zod 定义为准,并使用 getFormatInstructions 自动生成说明,减少人肉拼写的失误。

2. DeepSeek 的思考过程

如果你使用的是 deepseek-reasoner,它会包含一段 reasoning_content(思考过程)。LangChain 的 ChatDeepSeek 适配器通常会自动处理,但如果你发现输出中夹杂了思考逻辑,可以在 Prompt 中通过 JsonOutputParser 的指令强制约束其输出范围。


🌟 总结

通过这一套组合拳,我们完成了一个从非结构化需求结构化数据的转化。

  • Zod 负责定义数据的“骨架”。
  • PromptTemplate 负责包装“灵魂”。
  • DeepSeek 提供强大的“脑力”。
  • JsonOutputParser 则是最后的“质检员”。

这不仅是一个简单的脚本,更是所有复杂 AI Agent 的基石。当你学会了如何让 AI 输出可预测的数据,你就真正掌握了构建生产级 AI 应用的钥匙!