【LangChain】深入解析LangChain:如何使用输出解析器优化结果处理

2024-09-04 03:04

本文主要是介绍【LangChain】深入解析LangChain:如何使用输出解析器优化结果处理,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在这里插入图片描述

😁 作者简介:一名前端爱好者,致力学习前端开发技术
⭐️个人主页:夜宵饽饽的主页
❔ 系列专栏:JavaScript小贴士
👐学习格言:成功不是终点,失败也并非末日,最重要的是继续前进的勇气

​🔥​前言:

在使用langchain的过程中,输出解析器时非常关键的,可以帮助我们将复杂的模型响应转换为结构化、易于使用的数据格式,这是我自己的学习笔记,希望可以帮助到大家,欢迎大家的补充和纠正

文章目录

      • 1、使用StructuredOutputParser
      • 2、使用JsonOutputParser方法
      • 3、使用大模型自己的输出配置
      • 4、使用withStructuredOutput方法
      • 5、Tool calling工具调用

1、使用StructuredOutputParser

在模型响应中处理结构化数据的主要输出解析器类型是StructuredOutputParser,我们可以使用zod为我们期望从模型获得的输出类型定义一个架构

🍻实现步骤:

  1. 先构建好一个数据结构,一个你期望模型输出的对象格式
  2. 将定义好的数据结构传入StructuredOutputParser类中实例化
  3. 使用提示词模板,大模型,输出解析器构建工作流
import { z } from "zod";
import { RunnableSequence } from "@langchain/core/runnables";
import { StructuredOutputParser } from "@langchain/core/output_parsers";
import { ChatPromptTemplate } from "@langchain/core/prompts";const zodSchema = z.object({answer: z.string().describe("answer to the user's question"),source: z.string().describe("source used to answer the user's question, should be a website."),
});const azureModel=await getAzureModel()const parser = StructuredOutputParser.fromZodSchema(zodSchema);const chain = RunnableSequence.from([ChatPromptTemplate.fromTemplate("Answer the users question as best as possible.\n{format_instructions}\n{question}"),model,parser,
]);const response = await chain.invoke({question: "What is the capital of France?",format_instructions: parser.getFormatInstructions(),
});

分析以上的代码,关于parser.getFormatInstructions()这个方法的输出:

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":{"answer":{"type":"string","description":"answer to the user's question"},"source":{"type":"string","description":"source used to answer the user's question, should be a website."}},"required":["answer","source"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"}
```

可以看到输出解析器是使用提示词的方式来要求模型的输出,并还会验证其是否符合预期的结构化格式

这个StructuredOutputParser方法有一个缺点就是不支持流式处理

2、使用JsonOutputParser方法

让模型构建输出的最直观的方法是提供说明来描述您想要的输出类型,然后使用输出解析器解析输出,以将原始模型消息或字符串输出转换为更易于操作的内容

这样做有优点是:

  • 不需要任何特殊的模型特征,只要模型拥有足够的推理能力来理解传递的schema
  • 可以根据提示词输入任何需要输出的格式

不过也是有缺点的

  • 大模型是非确定性的。想要让模型始终如一地按照特定的格式输出内容并不容易,并且不同的大模型对提示的响应可能有所不同
  • 各个模型的特征和行为是不同的,这取决于它们的训练集,而且会在特定的数据格式上会有表现差异

🍻实现步骤:

  1. 准备好提示词,提示词必须有想要输出的示例说明
  2. 准备好输出解析器,解析器的数据结构可以使用接口类型来定义
  3. 构建好大模型工作流
const azureModel=await getAzureModel()let parse=new JsonOutputParser<Joke>();const formatInstructions="响应一个有效的JSON对象,包含两个字段:'setup' 和 'punchline'"const prompt=ChatPromptTemplate.fromTemplate(`回答这个用户的查询{formatInstructions}{query}`
)let partialedPrompt=await prompt.partial({formatInstructions:formatInstructions
})const chain=partialedPrompt.pipe(azureModel).pipe(parse)console.log(await chain.invoke({ query: '讲一个笑话吧' }))

使用JsonOutputParser这个输出解析器是,与StructuredOutputParser不同的是,JsonOutputParser支持流式输出

3、使用大模型自己的输出配置

目前有部分大模型直接可以在实例化模型的时候配置输出什么格式的数据

let azureModel=await getAzureModel()
const prompt=PromptTemplate.fromTemplate(`我将提供一些考试文本。请解析“问题”和“答案”,并以 JSON 格式输出它们。示例输入:世界上最高的山是哪座?珠穆朗玛峰。示例 JSON 输出,属性如下:"question": "世界上最高的山是哪座?","answer": "珠穆朗玛峰"输入:{input}`
)
const jsonAzureModel=azureModel.bind({response_format:{type:'json_object'}})const chain=prompt.pipe(jsonAzureModel)const result=await chain.invoke({input:'Which is the longest river in the world? The Nile River.'})
// console.log("🚀 ~ mainScript ~ result:", result)
console.log( result)

4、使用withStructuredOutput方法

一些 LangChain 聊天模型支持 .withStructuredOutput() 方法。此方法只需要 schema 作为输入,并返回与请求的 schema 匹配的对象。负责导入合适的输出解析器,并以适合模型的格式格式化架构。

const joke=z.object({setup:z.string().describe("The setup of the joke"),punchline:z.string().describe("The punchline of the joke"),rating:z.number().optional().describe("How funny the joke is,from 1 to 10")
})const azureModel=await getAzureModel()const structuredLlm=azureModel.withStructuredOutput(joke)// const structuredLlm = model.withStructuredOutput(joke, {
//   method: "json_mode",
//   name: "joke",
// });const result=await structuredLlm.invoke("Tell me a joke")
console.log("🚀 ~ mainScript ~ result:", result)

我建议在处理结构化输出是可以优先考虑这种方法,因为该方法相比其他的方法有一些优势

  • 它在后台使用特定于模型的功能,而无需导入输出解析器
  • 对于使用工具调用的模型,不需要特别的提示
  • 如果支持多种格式,可以使用method来切换
  • 我们可以提供架构的变量名,来说明schema代表的内容,从而提高性能

在使用构建JSON架构的时候,不想使用Zod的方式,可以考虑使用OpenAI风格的JSON架构,此对象包含三个属性:

  • name:输出的架构的名称
  • description:要输出的架构的高级描述
  • parameters:要提取的架构的嵌套详细信息,格式为JSON架构
const structuredLlm = model.withStructuredOutput({name: "joke",description: "Joke to tell user.",parameters: {title: "Joke",type: "object",properties: {setup: { type: "string", description: "The setup for the joke" },punchline: { type: "string", description: "The joke's punchline" },},required: ["setup", "punchline"],},

该方法也可以指定原始输出,大模型其实是不擅长生成结构化输出的,尤其是架构变得复杂是,我们可以使用原始输出来避免发生异常,可以配置includeRaw:true来实现

const structuredLlm = model.withStructuredOutput(joke, {includeRaw: true,name: "joke",
});/**
raw: AIMessage {lc_serializable: true,lc_kwargs: {content: "",tool_calls: [{name: "joke",args: [Object],id: "call_0pEdltlfSXjq20RaBFKSQOeF"}],invalid_tool_calls: [],additional_kwargs: { function_call: undefined, tool_calls: [ [Object] ] },response_metadata: {}},lc_namespace: [ "langchain_core", "messages" ],content: "",name: undefined,additional_kwargs: {function_call: undefined,tool_calls: [{id: "call_0pEdltlfSXjq20RaBFKSQOeF",type: "function",function: [Object]}]},response_metadata: {tokenUsage: { completionTokens: 33, promptTokens: 88, totalTokens: 121 },finish_reason: "stop"},tool_calls: [{name: "joke",args: {setup: "Why was the cat sitting on the computer?",punchline: "Because it wanted to keep an eye on the mouse!",rating: 7},id: "call_0pEdltlfSXjq20RaBFKSQOeF"}],invalid_tool_calls: [],usage_metadata: { input_tokens: 88, output_tokens: 33, total_tokens: 121 }},parsed: {setup: "Why was the cat sitting on the computer?",punchline: "Because it wanted to keep an eye on the mouse!",rating: 7}
}**/

我们还可以创建自定义的提示和解析器,使用plain函数解析模型的输出

import { AIMessage } from "@langchain/core/messages";
import { ChatPromptTemplate } from "@langchain/core/prompts";type Person = {name: string;height_in_meters: number;
};type People = {people: Person[];
};const schema = `{{ people: [{{ name: "string", height_in_meters: "number" }}] }}`;// Prompt
const prompt = await ChatPromptTemplate.fromMessages([["system",`Answer the user query. Output your answer as JSON that
matches the given schema: \`\`\`json\n{schema}\n\`\`\`.
Make sure to wrap the answer in \`\`\`json and \`\`\` tags`,],["human", "{query}"],
]).partial({schema,
});/*** Custom extractor** Extracts JSON content from a string where* JSON is embedded between ```json and ```tags.*/
const extractJson = (output: AIMessage): Array<People> => {const text = output.content as string;// Define the regular expression pattern to match JSON blocksconst pattern = /```json(.*?)```/gs;// Find all non-overlapping matches of the pattern in the stringconst matches = text.match(pattern);// Process each match, attempting to parse it as JSONtry {return (matches?.map((match) => {// Remove the markdown code block syntax to isolate the JSON stringconst jsonStr = match.replace(/```json|```/g, "").trim();return JSON.parse(jsonStr);}) ?? []);} catch (error) {throw new Error(`Failed to parse: ${output}`);}
};

5、Tool calling工具调用

对于支持工具调用的模型,我们可以使用工具调用来结构化输出,它消除了关于如何最好地提示 schema 以支持内置模型功能的猜测

实现步骤:

  1. 定义好数据结构,使用的是zod
  2. 在定义工具,不过传入的参数需要时JsonSchema格式的,所以需要转换
  3. 首先使用bind_tools方法直接绑定到聊天模型
  4. 然后该模型会生成一个AIMessage,其中就包含一个tool_calls字段,该字段包含所需形状匹配的args
    let toolSchema=z.object({answer:z.string().describe("The answer to the user's question"),followup_question:z.string().describe("A followup question the user could ask")})const model=await getAzureModel()const modelWithTools=model.bindTools([{type:"function",function:{name:"response_formatter",description:"Always use this tool to structure your response to the user.",parameters:zodToJsonSchema(toolSchema)}}])const aiMesssage=await modelWithTools.invoke("What is the powerhouse of the cell?")console.log("🚀 ~ mainScript2 ~ aiMesssage:", aiMesssage)

💪使用工具调用来进行输出数据格式化,与使用输出解析器的方式有些区别的:

  • 输出解析器是使用提示词来尝试约束模型的输出,然后在从模型的输出中解析出来所想要的JSON格式
  • 工具调用的话,会从根本上让模型根据绑定的工具来生成回答,而且目前大部分模型都内置啦工具调用的配置

这篇关于【LangChain】深入解析LangChain:如何使用输出解析器优化结果处理的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1134816

相关文章

全面解析HTML5中Checkbox标签

《全面解析HTML5中Checkbox标签》Checkbox是HTML5中非常重要的表单元素之一,通过合理使用其属性和样式自定义方法,可以为用户提供丰富多样的交互体验,这篇文章给大家介绍HTML5中C... 在html5中,Checkbox(复选框)是一种常用的表单元素,允许用户在一组选项中选择多个项目。本

Java对异常的认识与异常的处理小结

《Java对异常的认识与异常的处理小结》Java程序在运行时可能出现的错误或非正常情况称为异常,下面给大家介绍Java对异常的认识与异常的处理,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参... 目录一、认识异常与异常类型。二、异常的处理三、总结 一、认识异常与异常类型。(1)简单定义-什么是

Python并行处理实战之如何使用ProcessPoolExecutor加速计算

《Python并行处理实战之如何使用ProcessPoolExecutor加速计算》Python提供了多种并行处理的方式,其中concurrent.futures模块的ProcessPoolExecu... 目录简介完整代码示例代码解释1. 导入必要的模块2. 定义处理函数3. 主函数4. 生成数字列表5.

Python中help()和dir()函数的使用

《Python中help()和dir()函数的使用》我们经常需要查看某个对象(如模块、类、函数等)的属性和方法,Python提供了两个内置函数help()和dir(),它们可以帮助我们快速了解代... 目录1. 引言2. help() 函数2.1 作用2.2 使用方法2.3 示例(1) 查看内置函数的帮助(

Linux脚本(shell)的使用方式

《Linux脚本(shell)的使用方式》:本文主要介绍Linux脚本(shell)的使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录概述语法详解数学运算表达式Shell变量变量分类环境变量Shell内部变量自定义变量:定义、赋值自定义变量:引用、修改、删

Python包管理工具核心指令uvx举例详细解析

《Python包管理工具核心指令uvx举例详细解析》:本文主要介绍Python包管理工具核心指令uvx的相关资料,uvx是uv工具链中用于临时运行Python命令行工具的高效执行器,依托Rust实... 目录一、uvx 的定位与核心功能二、uvx 的典型应用场景三、uvx 与传统工具对比四、uvx 的技术实

Java使用HttpClient实现图片下载与本地保存功能

《Java使用HttpClient实现图片下载与本地保存功能》在当今数字化时代,网络资源的获取与处理已成为软件开发中的常见需求,其中,图片作为网络上最常见的资源之一,其下载与保存功能在许多应用场景中都... 目录引言一、Apache HttpClient简介二、技术栈与环境准备三、实现图片下载与保存功能1.

Python中使用uv创建环境及原理举例详解

《Python中使用uv创建环境及原理举例详解》uv是Astral团队开发的高性能Python工具,整合包管理、虚拟环境、Python版本控制等功能,:本文主要介绍Python中使用uv创建环境及... 目录一、uv工具简介核心特点:二、安装uv1. 通过pip安装2. 通过脚本安装验证安装:配置镜像源(可

LiteFlow轻量级工作流引擎使用示例详解

《LiteFlow轻量级工作流引擎使用示例详解》:本文主要介绍LiteFlow是一个灵活、简洁且轻量的工作流引擎,适合用于中小型项目和微服务架构中的流程编排,本文给大家介绍LiteFlow轻量级工... 目录1. LiteFlow 主要特点2. 工作流定义方式3. LiteFlow 流程示例4. LiteF

使用Python开发一个现代化屏幕取色器

《使用Python开发一个现代化屏幕取色器》在UI设计、网页开发等场景中,颜色拾取是高频需求,:本文主要介绍如何使用Python开发一个现代化屏幕取色器,有需要的小伙伴可以参考一下... 目录一、项目概述二、核心功能解析2.1 实时颜色追踪2.2 智能颜色显示三、效果展示四、实现步骤详解4.1 环境配置4.