AI探索实践12 - Typescript开发AI应用4:大模型响应数据的格式化输出

大家好,我是feng,感谢你阅读我的博文,如果你也关注AI应用开发,欢迎关注公众号和我一起​探索。如果文章对你有所启发,请为我点赞!

一、重点回顾

在介绍本文之前的文章中,我们先来回顾一下使用LangChain开发应用程序的逻辑:

  1. 创建一个模型对象(ChatOllama)。指定模型访问的URL、模型名称和temperature等主要参数

  2. 创建一个提示语模板对象(ChatPromptTemplate)

  3. 创建一个Chain对象:使用ChatPromptTemplate的pipe方法

  4. 调用Chain对象的invoke/batch/stream等方法,向模型发送请求并处理响应。

运行结果如图1

图1

注意内容字段的值。

二、问题发现

当我们调用LangChain时,通过输出我们可以发现,内容字段的值中包含有换行符   \n   ,有时会出现连接字符串的  号。这看起来并不美观,而且如果将这样的结果作为外部系统接口的输入可能还会造成不必要的异常和故障。

幸运的是LangChain提供了一个非常重要的功能 :Output Parsers(输出解析器)。通过输出解析器,我们可以控制从大模型返回的响应数据的结构和格式。无论我们期望输出的是数组、对象或其他的格式,LangChain都支持以你指定的方式来格式化这些内容。

接下来,我们来了解如何使用Typescript编写代码,来实现大模型输出内容的自定义格式化。

三、TS实现格式化输出

在网址 https://js.langchain.com/docs/modules/model_io/output_parsers/types/ ,我们可以看到LangChain支持的输出解析器列表,通常你应该可以找到你需要的特定用例的解析器。

我们重点来介绍3个解析器。

3.1 解析成字符串

如果希望将大模型的输出转为字符串,我们需要用到 StringOutputParser 类。代码实现非常简单:

import { StringOutputParser } from '@langchain/core/output_parsers';// 创建一个模型
const model = new ChatOllama({baseUrl: 'http://localhost:11434', // Default valuemodel: 'qwen:4b',
});// 其次,创建一个提示语模板对象。
const prompt = ChatPromptTemplate.fromMessages([['system', '根据用户提供的词语,来创作笑话'],['human', '{input}'],
]);

首先,我们需要导入 StringOutputParser 类。然后创建一个模型和提示语模板对象。为了增强代码的可读性,我们将不同的格式化实现,单独封装到一个函数里。

创建一个字符串格式化处理函数:

async function callStringOutputParser() {// 创建一个输出解析器const outputParser = new StringOutputParser();// 通过pipe创建一个chainconst chain = prompt.pipe(model).pipe(outputParser);// 这如同我们在编写java语言中的链式调用方法。在LangChain中,// 通过pipe将不同的对象都附加到整个链上,并且可以将前一个对象的输出,// 输入传递给后一个对象。// 上面代码中 prompt 的输出,作为  chatModel 的输入,// chatModel的输出,作为 parser的输入。return await chain.invoke({input: '小狗',});
}// 执行函数
const response = await callStringOutputParser();
// 打印大模型的响应
console.log(response);

代码中注释的比较清楚,我们需要声明一个 StringOutputParser 对象,并通过pipe方法附加到链上。这样在执行链时,大模型的推理结果就会作为输入传入到字符串输出解析器对象中,经过该对象进行处理,最终会输出字符串格式的数据。我们来观察一下结果:

图2

如图2所示,我们可以观察到和图1相比,经过 StringOutputParser 处理后 ,模型响应的结果只保留了内容字段的值,并且去掉了换行符等特殊字符,一个纯粹的字符串。

3.2 解析成数组

有时候,我们会希望传递给外部系统的数据格式为数组。对于这样的场景,LangChain提供了 CommaSeparatedListOutputParser 来支持。

实现逻辑与使用 StringOutputParser 相同。只是这一次我们进行一点改变,注意观察提示语模板的声明。

创建一个数组格式化处理函数:

import { CommaSeparatedListOutputParser } from '@langchain/core/output_parsers';async function callListOutputParser() {// 创建一个提示语模板对象const prompt = ChatPromptTemplate.fromTemplate(`Provide 3 synonyms, seperated by commas, for the following word {word}`);// 创建一个输出解析器:逗号分割的数组输出解析器const outputParser = new CommaSeparatedListOutputParser();// 通过pipe创建一个chainconst chain = prompt.pipe(model).pipe(outputParser);return await chain.invoke({word: 'happy',});
}// 执行函数
const response = await callListOutputParser();
// 打印大模型的响应
console.log(response, typeof response);

来观察输出结果,见图3:

图3 

提示语时要求大模型输出用户提供的词语的3个同义词。代码中,提供的是:happy。经过3次运行测试,大模型返回的是一个数组对象,包含3个元素。

注:在测试过程中,我发现2个问题,留待后续研究:

  1. 数组解析处理器,对于中文的分隔处理成数组还不稳定,会出现解析异常的情况。尚不确定是本地4B的千问模型处理能力比较弱的问题,或者是我的中文提示语写的有问题。因此,为了说清楚代码的实现逻辑,代码中暂时用英语来写提示语模板。
  2. 数组的最后一个元素,会出现句点符号。这应该是 CommaSeparatedListOutputParser 的实现逻辑的问题。

3.3 解析成json对象

json,是我们在日常开发过程中最常用的格式。LangChain提供了2种解析成Json格式的方法:

  1. 内置的,解析成简单的json对象
  2. Zod模式,通过第三方库Zod,解析成可嵌套的、复杂的json对象

要想解析为json对象,我们需要使用LangChain提供的结构化解析器 —  StructuredOutputParser

3.3.1 解析为简单json对象

创建一个结构化格式化处理函数:

import { StructuredOutputParser } from 'langchain/output_parsers';async function callStructuredParser() {const prompt = ChatPromptTemplate.fromTemplate(`从短语中提取信息。格式化说明: {desc}短语: {string}`);const outputParser = StructuredOutputParser.fromNamesAndDescriptions({name: '人的姓名',age: '人的年龄',address: '家庭住址',});const chain = prompt.pipe(model).pipe(outputParser);return await chain.invoke({string: '王山今年19岁了,住在科学大道208号5-4。',desc: outputParser.getFormatInstructions(),});
}// 执行函数
const response = await callStructuredParser();
// 打印大模型的响应
console.log(response, typeof response);

StructuredOutputParser 类只有2个方法实现对数据的格式化处理,其中 fromNamesAndDescriptions 方法是格式化成简单json对象。从名称可以看出函数主要是提取数据中的名称和对应的描述。

我们来理解一下 callStructuredParser 函数的逻辑:

1、当调用这个链时(也就是执行 chain.invoke方法),我们传入了2个参数:string 和 desc

名为 “string”,值为:“王山今年19岁了,住在科学大道208号5-4。”的字符串。这个值会被传入到提示语模板对象中的“短语”中的占位符变量。

名为“desc”,值为解析器的实例。该解析器实例规定了一个json对象的结构和取值的含义:如果发现“人的姓名”,则赋值给属性:“name”,如果发现“人的年龄”则赋值给属性:“age”,如果发现“家庭住址”则赋值给属性:“address”。

2、string 和 desc 传入到提示语模板中,结合模板中已经定义的大模型的指令:“从短语中提取信息”等,组成一个完整的提示语,并传递给大模型。

3、大模型接收到这个完整的提示语后,进行推理。并按要求提取了信息的语义,并将信息传递给解析器对象。

4、解析器对象根据预先定义好的json对象的结构和大模型提供的语义做出如下的动作:

  1. 将“王山”赋值给“name”
  2. 将“19岁”赋值给“age”
  3. 将“科学大道208号5-4”赋值给“address”
  4. 组成一个json对象,并返回

看看输出结果:

3.3.2 解析为可嵌套的、复杂的json对象 

我们需要借助Zod库来实现这个需求。在终端执行下面的命令。此处使用yarn,你当然可以使用npm进行安装。

yarn add zod

创建一个zod格式化处理函数:

import { z } from 'zod';async function callZodOutputParser() {const prompt = ChatPromptTemplate.fromTemplate(`从短语中提取信息。格式化说明: {desc}短语: {string}`);const outputParser = StructuredOutputParser.fromZodSchema(z.object({recipe: z.string().describe('食谱名称'),ingredients: z.array(z.string()).describe('组成部分'),}),);const chain = prompt.pipe(model).pipe(outputParser);return await chain.invoke({string: '意大利面的成分是西红柿、碎牛肉、大蒜、酒和香草',desc: outputParser.getFormatInstructions(),});
}// 执行函数
const response = await callZodOutputParser();
// 打印大模型的响应
console.log(response, typeof response);

这是一个分析食谱的请求。在定义输出解析器时,使用了 StructuredOutputParser.fromZodSchema ,参数是一个对象定义:

z.object,代表了格式化返回的是一个对象类型。它的结构自定义为:

  1. 有一个名为 recipe 的属性,这个属性是字符串类型,代表含义是“食谱名称”,
  2. 有一个名为ingredients的属性,这个属性是数组类型,代表的含义是“组成部分”

看看执行结果:

至此,我们实现了使用Typescript和LangChain对大模型响应数据的自定义格式化的目标。

四、总结

对于大模型响应数据的格式化处理,是我们在开发AI应用时应该掌握的技能。通常情况下,我们不会仅仅依靠大模型来实现交互,而是会将一些大模型推理后的数据交付给AI应用外部的系统进行处理。这些系统通常是企业中已存在的系统。

但是,我们也看到一些问题。LangChain在处理中文时,还存在一些不完善。我们必须在应用中发现并完善这些点。随着LangChain的升级,相信前端工程师能够在AI大模型开发中,承担越来越重要的职责。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/731566.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Google OAuth2.0获取授权信息返回的id_token如何解析

背景 我们在做google登录的时候,第一步先要获取授权码code,在得到code之后会根据code拿到授权的token信息,token信息中包含:access_token,refresh_token,scope,token_type,expires_…

两天学会微服务网关Gateway-Gateway过滤器

锋哥原创的微服务网关Gateway视频教程: Gateway微服务网关视频教程(无废话版)_哔哩哔哩_bilibiliGateway微服务网关视频教程(无废话版)共计17条视频,包括:1_Gateway简介、2_Gateway工作原理、3…

前端React篇之React中什么是受控组件和非控组件?

目录 React中什么是受控组件和非控组件?受控组件非受控组件 React中什么是受控组件和非控组件? 在React中,受控组件和非受控组件是两种表单元素的状态管理方式。 受控组件 受控组件(Controlled Component)是指其值由…

探秘Solr:解密搜索引擎背后的原理与应用(一)

本系列文章简介: 在本系列文章中,我们将从Solr的基本概念开始,解释索引和搜索是如何工作的,深入探讨倒排索引、分词器和搜索算法等关键概念。接着,我们将了解Solr的工作原理,从数据导入和索引构建到搜索流程…

数据结构 - 栈和队列

本篇博客将介绍栈和队列的定义以及实现。 1.栈的定义 栈是一种特殊的线性表,只允许在固定的一端进行插入和删除数据,插入数据的一端叫做栈顶,另一端叫做栈底。栈中的数据遵守后进先出的原则 LIFO (Last In First Out)。 插入数据的操作称为压…

如何借助私域营销在医美行业中脱颖而出?

在现今这个以貌取人的社会,外貌焦虑已变得司空见惯。美丽往往能给人带来更多的瞩目和机遇,但天生丽质并非人人可得。随着经济的繁荣和消费结构的升级,颜值经济开始崭露头角,医美行业因此受到了广大消费者的青睐,迎来了…

Leetcode 剑指 Offer II 068.搜索插入位置

题目难度: 简单 原题链接 今天继续更新 Leetcode 的剑指 Offer(专项突击版)系列, 大家在公众号 算法精选 里回复 剑指offer2 就能看到该系列当前连载的所有文章了, 记得关注哦~ 题目描述 给定一个排序的整数数组 nums 和一个整数目标值 target &#xf…

阿波罗登月需要解决飞行控制问题,数学家卡尔曼在维纳控制的基础上提出了卡尔曼滤波

说到登月,很多人只想到和火箭以及航天器相关的技术,其实登月离不开信息技术的革命。因为从飞行控制到远程通信,都需要解决很多过去从未遇到过的难题。 登月首先要保证在月球上着陆的地点准确,而且要保证返回火箭和飞船能够在月球轨…

【LeetCode: 211. 添加与搜索单词 - 数据结构设计 + 前缀树】

🚀 算法题 🚀 🌲 算法刷题专栏 | 面试必备算法 | 面试高频算法 🍀 🌲 越难的东西,越要努力坚持,因为它具有很高的价值,算法就是这样✨ 🌲 作者简介:硕风和炜,…

npm install没有创建node_modules文件夹

问题记录 live-server 使用时 报错:live-server : 无法将“live-server”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。 npm install 安装 但是 这时npm install没有创建node_modules文件夹,只生成package-lock.json文件 方法一: 手…

CEF c++调用JS,并获得JS返回值的 流程

相关章节: CEF JS与c++能够交互的原理 以及 JS 调用C++的流程分析 CEF 之 Render进程 与 Browser进程通信 目录

【笔试48天强训】day05

统计回文1 “回文串”是一个正读和反读都一样的字符串,比如“level”或者“noon”等等就是回文串。花花非常喜欢这种拥有对称美的回文串,生日的时候她得到两个礼物分别是字符串A和字符串B。现在她非常好奇有没有办法将字符串B插入字符串A使产生的字符串…

通过Electron打包前端项目为exe

🧑‍🎓 个人主页:爱蹦跶的大A阿 🔥当前正在更新专栏:《JavaScript保姆级教程》、《VUE》、《Krpano》 ✨ 正文 1、 拉取electron官网上的demo,拉下来之后安装依赖,项目跑起来之后,就…

第十篇 - 如何利用人工智能技术做好营销流量整形管理?(Traffic Shaping)- 我为什么要翻译介绍美国人工智能科技巨头IAB公司

IAB平台,使命和功能 IAB成立于1996年,总部位于纽约市​​​​​​​。 作为美国的人工智能科技巨头社会媒体和营销专业平台公司,互动广告局(IAB- the Interactive Advertising Bureau)自1996年成立以来,先…

上海计算机学会竞赛平台2024.1月月赛丙组T1 成绩等第题解

题目描述 给定一个在到之间的整数 ,请将它转成等第,规则如下: 为 A为 B为 C为 D为 F 输入格式 单个数字表示 输出格式 单个字符表示答案 数据范围 样例数据 输入: 93 输出: A 输入: 44 输出: F

公网IP与私有IP及远程互联

1.公网有私有IP及NAT 公网IP是全球唯一的IP,通过公网IP,接入互联网的设备是可以访问你的设备。但是IPV4资源有限,一般ISP(Internet Service Provider)并不会为用户提供公网IP。所以家里的计算机在公司是没法直接使用windows远程桌面直接访问…

Arrays类常用方法详解(源码分析)

Arrays.equals() 众所周知,equals方法是比较里面的内容是否相等的方法,那Arrays中的equals()方法,就是重写了Object中equals(),用来比较数组中的内容是否相等…

Find My产品越来越得到市场认可,伦茨科技ST17H6x芯片赋能厂家

苹果发布AirTag发布以来,大家都更加注重物品的防丢,苹果的 Find My 就可以查找 iPhone、Mac、AirPods、Apple Watch,如今的Find My已经不单单可以查找苹果的设备,随着第三方设备的加入,将丰富Find My Network的版图。产…

LCR 164. 破解闯关密码

解题思路&#xff1a; 贪心 class Solution {public String crackPassword(int[] password) {String[] strs new String[password.length];for(int i 0; i < password.length; i)strs[i] String.valueOf(password[i]);Arrays.sort(strs, (x, y) -> (x y).compareTo(…

考研复习c语言初阶(1)

本人准备考研&#xff0c;现在开始每天更新408的内容&#xff0c;目标这个月结束C语言和数据结构&#xff0c;每天更新~ 一.再次认识c语言 C语言是一门通用计算机编程语言&#xff0c;广泛应用于底层开发。C语言的设计目标是提供一种能以简易 的方式编译、处理低级存储器、产生…