写给自己的 LangChain 开发教程(二):格式化数据 & 提取 & 分类

这个教程是一边学习一边写的,中间可能会出现一些疏漏或者错误,如果您看到该篇文章并且发现了问题,还请多多指教!

1. 格式化数据

在实际应用中,我们的应用的前端和后端之间的数据交换是一个确切参数的 json 数据,我们可能会期望大模型将不可控的用户输入,转换成我们定义好的某个规整结构进行输出,这是一个很常见的需求,我们可以通过 LangChain 结合 Zod 或者 JSON Scheme 来实现我们的需求。

a. Zod

Zod 是一个 转为 Typescript 应用设计的校验库

如果我们希望格式化数据,可能的流程是这样

  1. 定义一个工具函数 format_tool
  2. 根据用户输入的内容来决定是否需要调用 tool 转换
  3. 解析模型输出的内容转换为最终输出的 json

LangChain js-sdk 对 Zod 进行了深度的集成,提供了内置的 withStructuredOutput api 来让我们可以方便的格式化输入内容至我们定义好的数据。

ts 复制代码
import { ChatOllama } from '@langchain/ollama'
import { z } from 'zod'
​
const llm = new ChatOllama({
  model: 'qwen3:32b'
})
​
const personScheme = z.object({
  name: z.string().describe('The name of the person'),
  age: z.number().describe('The age of the person')
})
​
const structuredLLM = llm.withStructuredOutput(personScheme, {
  // 一定要传这个参数,告诉模型以纯json的格式输出,不然可能会报错 OUTPUT_PARSING_FAILURE
  method: 'json_mode'
})
​
const result  = await structuredLLM.invoke('他的姓名是tocka,今年26岁了')
console.log (result)
// 输出 { age: 26, name: 'tocka' }

b. JSON Scheme

JSON Scheme 是在前后端需要使用同一套校验逻辑的时候经常使用的结构,不需要借助外部库的情况下我们也能很轻松的看明白它所表达的内容。在 LangChain 中使用 JSON Scheme 的方式来格式化数据会稍微麻烦一点。

ts 复制代码
import { ChatOllama } from '@langchain/ollama'
import { JsonOutputParser } from '@langchain/core/output_parsers'
import { ChatPromptTemplate } from '@langchain/core/prompts'
​
const llm = new ChatOllama({
  model: 'qwen3:32b'
})
​
const personScheme = {
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "description": "The name of the person.",
      "minLength": 1
    },
    "age": {
      "type": "number",
      "description": "The age of the person.",
      "minimum": 0
    }
  },
  "required": ["name", "age"]
}
const parser = new JsonOutputParser()
​
const promptTemplate = ChatPromptTemplate.fromTemplate(`
Extract the person information from the following text and return it as JSON according to this schema:
​
Schema: {schema}
​
Text: {text}
​
Please return only valid JSON that matches the schema.
`)
​
const prompt = await promptTemplate.invoke({
  schema: personScheme,
  text: '他的姓名是tocka,今年26岁了'
})
​
const output = await llm.invoke(prompt)
const result = await parser.invoke(output)
​
console.log (result)
// 输出 { age: 26, name: 'tocka' }

LangChain 为每个可运行的组件实现了 .pipe() 方法,用来构建一个流程链,当我们调用链的时候它的组件会依次调用每个组件的 invoke 方法,将返回值传给链上的下一个组件,所以我们的代码可以简化一下

ts 复制代码
const chain = promptTemplate.pipe(llm).pipe(parser)
const result = await chain.invoke({
  schema: personScheme,
  text: '他的姓名是tocka,今年26岁了'
})
​
console.log (result)
// 输出 { age: 26, name: 'tocka' }

2. 提取 & 分类

在格式化数据的应用场景中,提取内容和进行分类也是一种很常见的需求,想象我们正在管理一个商品,这个商品有很多条评价,而评价有好评,中评,差评,这三个纬度可能是从评分直接展示,也有可能是需要根据用户的语义来分析获取的,现在你需要根据评价的内容,将评价转换为一个 { 评价总体倾向,优点,缺点 } 的 json 数据,假设我们的商品是一款电子手表。

ts 复制代码
import { ChatOllama } from '@langchain/ollama'
import { JsonOutputParser } from '@langchain/core/output_parsers'
import { ChatPromptTemplate } from '@langchain/core/prompts'
import { z } from 'zod'
​
const llm = new ChatOllama({
  model: 'qwen3:32b'
})
​
// 使用 nullish 让模型可以将字段置为 null 而不是乱编
const schema = z.object({
  sentiment: z.enum(['positive','neutral','negative']).describe('评论对商品的总体评价倾向'),
  advantage: z.nullish(z.string()).describe('评论提到的具体的优点'),
  disadvantage: z.nullish(z.string()).describe('评论提到的具体的缺点'),
})
​
const data = [
  "这款手表的电池续航太棒了,充一次电能用好几天。",
  "手表外观很时尚,佩戴起来也很舒适,但心率监测功能似乎不太准。",
  "考虑到价格,这块表还不错,不过打开应用时有点慢。",
  "我喜欢它的设计,屏幕也很清晰。运动追踪功能非常详细。",
  "软件有很多bug,经常卡顿闪退。我对它的性能很失望。"
]
​
const structuredLLM = llm.withStructuredOutput(schema, {
  method: 'json_mode'
})
​
const prompt = ChatPromptTemplate.fromTemplate(`
    本商品是一款电子手表,以下输入是用户对它的评价,请从中提取商品的优点和缺点,并判断评价的整体倾向
    input: {commend}
  `)
​
const chain = prompt.pipe(structuredLLM)
​
const result = data.map((commend) => chain.invoke({ commend }))
​
console.log(await Promise.all(result))
json 复制代码
// 输出:
[
  {
    advantage: '电池续航太棒了,充一次电能用好几天',
    disadvantage: null,
    sentiment: 'positive'
  },
  {
    advantage: '手表外观时尚,佩戴舒适',
    disadvantage: '心率监测功能不太准',
    sentiment: 'neutral'
  },
  {
    advantage: '考虑到价格,这块表还不错',
    disadvantage: '打开应用时有点慢',
    sentiment: 'neutral'
  },
  {
    advantage: '设计、屏幕清晰、运动追踪功能详细',
    disadvantage: null,
    sentiment: 'positive'
  },
  { disadvantage: '软件有很多bug,经常卡顿闪退', sentiment: 'negative' }
]

3. 小结

通过本篇我们学习了如何通过 ZodJSON Scheme 格式化数据,这部份内容虽然比较基础,但是在我们编排流程时会经常用到,例如根据用户的输入来构建查询参数等等。

相关推荐
广西生活网2 分钟前
水晶光电发布 2026 半年报:归母净利润 5.24 亿元同比增长 4.73%
人工智能·能源
梦曦i3 分钟前
RouterLink v2.5.0:H5端原生能力全面回归
前端·uni-app
浩腾数字多媒体7 分钟前
如何判断专业电子留言厂家适配条件?
大数据·人工智能·python
阿里云大数据AI技术12 分钟前
从 Common Crawl 到可训练语料:用 EMR Serverless Daft 构建清洗、模型标注与向量化管线
人工智能·spark
小林ixn17 分钟前
React + Zustand + JWT:从零实现登录鉴权与请求拦截
前端·react.js·前端框架
jay神18 分钟前
27届计算机专业深度学习选题推荐
人工智能·深度学习·yolo·机器学习·计算机视觉
stuartevil20 分钟前
AI漫剧日更制作怎么做才不累
人工智能
葡萄城技术团队21 分钟前
下拉多选类型单元格:在 SpreadJS 里接入 xm\-select
前端
大模型任我行22 分钟前
NUS:全模态感知驱动AI科学家
人工智能·语言模型·自然语言处理·论文笔记
weixin_4462608523 分钟前
G‑MARK:基于知识图谱的协同自动驾驶接地多智能体推理框架
人工智能·自动驾驶·知识图谱