Langchain.js | StructedOutputParser | 疯狂输出(二)

前言

书接上文 , 学习模型输出格式 AIMessage 以及 StringOutputParser 的意义 , 这一次继续输出 , 如何使得模型的输出结果结构化 , 关于输入(promptTemplate)的结构化 ,在前些文章中 ,也讲到 , 可见结构化是多么重要 , 因为只有统一的输出格式 , 我们在面对复杂情况的时候 , 经过大模型的处理的到的是统一的结果 , 这样为编码带来很大的方便

比如 , 后端的响应结果 , 我们通过 ,会封装为

js 复制代码
{
  code : 200 ,
  data : [] ,
  msg : "ok"
}

这样的形式 , 方便前端知道后端的响应情况, 响应结果 , 只要是获取数据就从 data 里面取 ,

这就是输出结构化带来的方便

在 LangChain.js/@langchain/core/out_pasers包下 , 有这样的一个类 ------

StructuredOutputParser (它的 api 点👈)

如下图 , 就是他在out_pasers 的真容

v03.api.js.langchain.com/modules/_la...

宏观体验

我想要输出这样的格式 , 大家可以应实际场景自定义 :

js 复制代码
 {
        answser : "用户问题的答案",
        evidence: "你回答用户问题所依据的答案",
        confidence: "问题答案的可信度评分,格式是百分数",
        source: "回答问题的来源,可以是知识库,也可以是互联网 ,或者其他的,可选"
}

直接看代码

js 复制代码
import {StructuredOutputParser} from "langchain/output_parsers";
import {PromptTemplate} from "@langchain/core/prompts";
import {ChatOpenAI} from "@langchain/openai";
//0.实例化一个模型
const model  = new ChatOpenAI();
//1.创建一个结构化输出解析器
const parser = StructuredOutputParser.fromNamesAndDescriptions(
    {
        answser : "用户问题的答案",
        evidence: "你回答用户问题所依据的答案",
        confidence: "问题答案的可信度评分,格式是百分数",
        source: "回答问题的来源,可以是知识库,也可以是互联网 ,或者其他的,可选"
    }
); 
//2.构建提示词
const prompt = PromptTemplate.fromTemplate(
    " 请回答这个问题: {question}且严格按照下面的指导 {instructions}"
)
//3.构建一个Chain
const chain = prompt.pipe(model).pipe(parser); // pipe()方法将prompt和model连接起来,并返回一个Chain对象
// 4.调用chain,传入参数
const res = await chain.invoke({
    question: "你知道东华理工大学吗?请具体说说",
    instructions: parser.getFormatInstructions()
})
//5.打印结果
console.log(res)

大模型的回复 :

分而治之

把构造 prompt 和 parser 的部分拿出来 , 也就是只看输入输出

js 复制代码
//1.创建一个结构化输出解析器
const parser = StructuredOutputParser.fromNamesAndDescriptions(
    {
        answser : "用户问题的答案",
        evidence: "你回答用户问题所依据的答案",
        confidence: "问题答案的可信度评分,格式是百分数",
        source: "回答问题的来源,可以是知识库,也可以是互联网 ,或者其他的,可选"
    }
); 
//2.构建提示词
const prompt = PromptTemplate.fromTemplate(
    " 请回答这个问题: {question}且严格按照下面的指导 {instructions}"
)

上面代码 , 控制了一次输入输出的格式 , 这就是一种 AI 时代的编程范式

我们通过在 promtTemplate 中预先设置好变量 , 在调用的时候填充 , 如下:

js 复制代码
//3.构建一个Chain
const chain = prompt.pipe(model).pipe(parser); // pipe()方法将prompt和model连接起来,并返回一个Chain对象
// 4.调用chain,传入参数
const res = await chain.invoke({
    question: "你知道东华理工大学吗?请具体说说",
    instructions: parser.getFormatInstructions()
})
//5.打印结果
console.log(res)

如此可以获取上面的结果

上面的 instructions ,到底填充了什么 ?

打印出来一探究竟 !

一探究竟

js 复制代码
console.log(parser.getFormatInstructions())
js 复制代码
You must format your output as a JSON value that adheres to a given "JSON Schema" instance.

"JSON Schema" is a declarative language that allows you to annotate and validate JSON documents.

For example, the example "JSON Schema" instance {{"properties": {{"foo": {{"description": "a list of test words", "type": "array", "items": {{"type": "string"}}}}}}, "required": ["foo"]}}}}
would match an object with one required property, "foo". The "type" property specifies "foo" must be an "array", and the "description" property semantically describes it as "a list of test words". The items within "foo" must be strings.
Thus, the object {{"foo": ["bar", "baz"]}} is a well-formatted instance of this example "JSON Schema". The object {{"properties": {{"foo": ["bar", "baz"]}}}} is not well-formatted.

Your output will be parsed and type-checked according to the provided schema instance, so make sure all fields in your output match the schema exactly and there are no trailing commas!

Here is the JSON Schema instance your output must adhere to. Include the enclosing markdown codeblock:
```json
{"type":"object","properties":{"answser":{"type":"string","description":"用户问题的答案"},"evidence":{"type":"string","description":"你回答用户问题所依据的答案"},"confidence":{"type":"string","description":"问题答案的可信度评分,格式是百分数"},"source":{"type":"string","description":"回答问题的来源,可以是知识库,也可以是互联网 ,或者其他的,可选"}},"required":["answser","evidence","confidence","source"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"}
```

仔细阅读发现 ,这也是一段 promtTemplate , 这段提示词中

js 复制代码
Here is the JSON Schema instance your output must adhere to. 
Include the enclosing markdown codeblock:
```json
{"type":"object","properties":{"answser":{"type":"string","description":"用户问题的答案"},"evidence":{"type":"string","description":"你回答用户问题所依据的答案"},"confidence":{"type":"string","description":"问题答案的可信度评分,格式是百分数"},"source":{"type":"string","description":"回答问题的来源,可以是知识库,也可以是互联网 ,或者其他的,可选"}},"required":["answser","evidence","confidence","source"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"}
```

是在说 : 这是 JSON 模式实例,你的输出必须遵循它。包括包围的 Markdown 代码块。

JSON 模式实例 中便是我们创建一个结构化输出解析器时候输入的

js 复制代码
//1.创建一个结构化输出解析器
const parser = StructuredOutputParser.fromNamesAndDescriptions(
    {
        answser : "用户问题的答案",
        evidence: "你回答用户问题所依据的答案",
        confidence: "问题答案的可信度评分,格式是百分数",
        source: "回答问题的来源,可以是知识库,也可以是互联网 ,或者其他的,可选"
    }
); 

Zod

How to use output parsers to parse an LLM response into structured format

在StructedOutputParser 中 , 有这样的方法 , 可以在规定输出结构的同时 , 还可以一些输出的限制 , 而限制就需要用到 Zod

Zod 是一个用于 JavaScript 和 TypeScript 的类型声明与验证库 , 其核心功能之一是对数据进行验证,确保实际传入的数据符合预先定义好的类型规范。

ini 复制代码
const parser = StructuredOutputParser.fromZodSchema(schema);

借助 StructuredOutputParser 并基于前面定义好的 Zod 架构 schema 创建了一个结构化输出解析器。

这个解析器的作用是,后续当接收到数据(比如从语言模型返回的数据)时,它能够依据 Zod 架构所设定的规则去验证数据是否符合预期的结构和类型要求,如果不符合,可能会抛出相应的错误或者进行相应的处理,只有符合要求的数据才能通过解析器进一步流转到后续的代码逻辑中使用。

这里对 answer 与 confidence 进行类型限制

answer 必须是字符串

confidence : 必须是数字 ,并且范围在 0-100

js 复制代码
const schema = z.object(
  { 
    answer: z.string().describe("用户问题的答案"), 
    confidence: z.number().min(0).max(100).describe("问题答案的可信度评分在范围0-100之间,精确到一位小数")
  });

如下代码 :

js 复制代码
import { z } from "zod";
import { StructuredOutputParser } from "@langchain/core/output_parsers";
import { PromptTemplate } from "@langchain/core/prompts";
import { ChatOpenAI } from "@langchain/openai";


// 1. 定义一个 zod 架构 , 对输出进行限制
const schema = z.object({
  answer:  z.string().describe("用户问题的答案"),
  confidence: z.number().min(0).max(100).describe("问题答案的可信度评分在范围0-100之间,精确到一位小数")
});
// 2. 使用StructuredOutputParser创建一个结构化输出解析器
const parser = StructuredOutputParser.fromZodSchema(schema);
const prompt = PromptTemplate.fromTemplate("回答该问题{question},严格按照{instructions}")
const model = new ChatOpenAI()
const chain = prompt.pipe(model).pipe(parser);
const res = await chain.invoke({
    question:"你吃了饭吗?",
    instructions: parser.getFormatInstructions()
})
console.log(res);
js 复制代码
{ answer: "我吃饭了", confidence: 95.5 }

总结

即使疯狂输出 , 也必须按照一定的规则 👈 , 方便处理

相关推荐
一只小爪子24 分钟前
通过 ulimit 和 sysctl 调整Linux系统性能
linux·运维·前端
YONG823_API26 分钟前
如何通过API实现淘宝商品评论数据抓取?item_review获取淘宝商品评论
大数据·开发语言·javascript·数据库·网络爬虫
一点一木31 分钟前
前端报告 2024:全新数据,深度解析未来趋势
前端·javascript·vue.js
失眠的咕噜34 分钟前
vue 导出excel接口请求和axios返回值blob类型处理
前端·javascript·vue.js
HelloZheQ1 小时前
CSS 伪类和伪元素:为你的选择器注入更多活力
前端·css
nt11071 小时前
一次性上传 1000 张图片, 总量 10GB 的方案设计
前端
吃杠碰小鸡1 小时前
css中的部分文字特性
前端·css
济南小草根1 小时前
JavaScript学习记录10
开发语言·javascript·学习
JINGWHALE11 小时前
设计模式 行为型 命令模式(Command Pattern)与 常见技术框架应用 解析
前端·人工智能·后端·设计模式·性能优化·系统架构·命令模式
$程2 小时前
【React】漫游式引导
前端·javascript·react.js