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,一经查实,立即删除!

相关文章

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

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

数据结构 - 栈和队列

本篇博客将介绍栈和队列的定义以及实现。 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文件 方法一: 手…

通过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远程桌面直接访问…

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语言的设计目标是提供一种能以简易 的方式编译、处理低级存储器、产生…

WordPress建站入门教程:如何选择和设置固定链接结构?

我们成功搭建好WordPress网站后&#xff0c;发布的文章对应的URL地址默认是使用“日期和名称型”&#xff0c;即是网站域名跟着的是年月日&#xff0c;最后是文章标题&#xff0c;如http://www.yigujin.com/2024/03/06/免费响应式WordPress博客主题JianYue/ 为了让我们的文章U…

委员建议进一步扩大香港与内地金融市场互联互通发展

在我们共同追寻金融发展的星辰大海之际&#xff0c;我怀着无比激动的心情&#xff0c;向诸位委员提议进一步扩大香港与内地金融市场互联互通发展。这个议题犹如一颗璀璨的明珠&#xff0c;闪耀着诱人的光芒&#xff0c;吸引着我们为之奋斗。让我们共同探讨这一话题&#xff0c;…

在【IntelliJ IDEA】中配置【Tomcat】【2023版】【中文】【图文详解】

作为一款功能强大的集成开发环境&#xff08;IDE&#xff09;&#xff0c;IntelliJ IDEA为Web服务器提供了卓越的支持&#xff0c;从而极大地简化了程序员在Web开发过程中的工作流程。学习Java Web开发实质上就是掌握如何创造动态Web资源&#xff0c;这些资源在完成开发后&…

server win搭建apache网站服务器+php网站+MY SQL数据库调用电子阅览室

一、适用场景&#xff1a; 1、使用开源的免费数据库Mysql&#xff1b; 2、自己建网站的发布&#xff1b; 3、使用php代码建网站&#xff1b; 4、使用windows server作为服务器&#xff1b; 5、使用apache作为网站服务器。 二、win server 中apache网站服务器搭建 &#xff0…

UUU.咕咚视频批量混剪软件下载,批量剪辑个体无限批量生成,批量合成视频,批量混剪视频的软件是什么样的?批量剪辑软件在哪下载?批量混剪软件下载容易吗?

[md]# 前言 工具的产生源于dy出的火山引擎的云视频混剪制作是按分钟数收费的&#xff0c;这个软件既能实现正常混剪也能避免二次收费。属于FFMPEG合成的。 欢迎大家给一些好的建议和功能&#xff0c;回复可见&#xff0c;附加了一些天卡&#xff0c;周卡&#xff0c;请大家不要…

excel统计分析——重复测量设计

参考资料&#xff1a;生物统计学 裂区设计中的裂区通常是指空间上的裂区&#xff0c;如果对试验指标进行连续测量时&#xff0c;时间也可以作为裂区因素。重复测量设计实际上就是时间裂区设计。进行试验结果的统计分析时&#xff0c;将试验因素作为主区&#xff0c;时间因素作为…