用最精简的代码实现可读取本地资料的AI聊天机器人
本文章将通过一个最精简的案例来演示可读取本地数据的AI聊天机器人的核心开发流程。该案例通过获取vue3最新官网的一页数据,并将数据存储在Pinecone向量库中,实现聊天式学习官网最新的vue3知识。
github开源地址:github.com/GreysonHYH/…
实现背景:
1.由于ChatGPT的内容最新截止到2021年9月,而在这之后的互联网上最新的信息它是不知道的,而通过让它读取本地资料可获取到最新的数据。
2.当我们需要学习某一个特定的材料和文档时,可以让AI读取该材料,以实现聊天式学习。
3.提高我们查找本地资料的速度,以及让AI更懂用户,因为它还会对我们本地的数据信息进行总结。
4.未来开发者乃至各行各业工作者都离不开AI,有必要了解对于如何构建一个AI助手的基本实现思路。
实现思路:
一、将本地数据存进Pinecone向量数据库
1.将本地数据进行切块。
2.切块完成后经过OpenAI Embedding转换成向量,存进向量库中。
二、提问题,生成答案
1.将问题经过OpenAI Embedding转换成向量,进入到向量库中查找。
2.取出查找到的相关的数据资料,与提出的问题一起喂给GPT3.5/4。
3.GPT3.5/4根据喂给它的数据资料与提出的问题来生成答案。
实现步骤:
一、将本地数据库存进Pinecone向量数据库。
1.初始化项目
初始化项目,生成package.json文件,并为项目安装langchain。
npm init
npm i langchain
langchain支持python也支持node.js,该案例中我们采用node.js。 在package.json中添加type属性,值为module。
2.读取本地资料。
创建ingest-data.js文件,ingest-data.js文件中引入 UnstructuredLoader 用于读取本地资料。“./vue3-document.md”为本地markdown文档。unstructuredLoader.load()为一个promise,返回一个数组,数组每一项为将markdown文档拆分后的文档对象。
import { UnstructuredLoader } from "langchain/document_loaders/fs/unstructured"
const unstructuredLoader = new UnstructuredLoader("./vue3-document.md")
const rawDocs = await unstructuredLoader.load()
3.对本地资料进行切块。
引入 RecursiveCharacterTextSplitter 用于对本地资料进行切块。参数chunkSize的值表示切分的大小,数值太大会导致查询过慢的问题,还有可能会超过大语言模型的长度限制而发生报错。如果设置太小也会导致信息太碎片化,得到的结果有点胡说八道的感觉。这个值就需要居于自身的内容进行调整。参数chunkOverlap的值表示切出来的每一块可与上一块或下一块的可重叠的大小。
import { RecursiveCharacterTextSplitter } from "langchain/text_splitter"
const splitter = new RecursiveCharacterTextSplitter({
chunkSize:1000,
chunkOverlap:200
})
const docs = await splitter.splitDocuments(rawDocs)
4.将切块后的文档存入到Pinecone向量数据库中。
先创建一个.env文件,用于存放环境变量。
OPENAI_API_KEY = "xxx"
PINECONE_API_KEY = "xxx"
PINECONE_ENVIRONMENT = "xx-xxx-xx"
PINECONE_INDEX = "greyson"
回到ingest-data.js,配置环境变量。
import dotenv from 'dotenv'
dotenv.config()
安装Pinecone向量数据库。
npm install -S @pinecone-database/pinecone
引入 PineconeClient, 初始化Pinecone向量数据库。
import { PineconeClient } from "@pinecone-database/pinecone"
const pineconeClient = new PineconeClient()
await pineconeClient.init({
apiKey: process.env.PINECONE_API_KEY,
environment: process.env.PINECONE_ENVIRONMENT,
})
要填写apiKey和environment的值,需要注册Pinecone,在API KEY中获取。
创建一个index,并拿到该index名称。
const pineconeIndex = pineconeClient.Index("greyson")
将本地文档存入Pinecone向量数据库。
PineconeStore.fromDocuments(docs,new OpenAIEmbeddings(),{
pineconeIndex,
textKey:"text",
namespace:"vue3-document"
})
执行代码。
node ingest-data.js
打开对应的index查看,向量总数发生改变,说明已经成功导入向量数据库。
以上完成将本地资料存储到Pinecone向量数据库。
二、提出问题,生成答案。
新建chat.js文件。创建OpenAI语言模型,temperature的值为控制下一个字符的准确度,如果为0就是将最准确的下一个字符匹配过来,如果数值较大,匹配的字符更随机,将有可能生成更具创造性的句子,可能更适合用来编写文章。对于本次案例来说,设置为0比较合适。
import { OpenAI } from "langchain/llms/openai"
const model = new OpenAI({
temperature:0
})
创建Pinecone向量数据库,之后用于根据问题检索里面的数据。
const pineconeClient = new PineconeClient()
await pineconeClient.init({
apiKey: process.env.PINECONE_API_KEY,
environment: process.env.PINECONE_ENVIRONMENT,
})
const pineconeIndex = pineconeClient.Index(process.env.PINECONE_INDEX)
const pineconeStore = await PineconeStore.fromExistingIndex(new OpenAIEmbeddings(),{
pineconeIndex,
textKey:"text",
namespace:"vue3-document"
})
创建一条聊天检索交互链,分别放入大语言模型(即OpenAI),向量数据库检索器以及配置项对象。
将returnSourceDocuments设为true,即表示返回生成的回答时,将所参考到的原材料一并返回。
const chain = ConversationalRetrievalQAChain.fromLLM(model,pineconeStore.asRetriever(),{
returnSourceDocuments:true
})
提问题,question值为所提出的问题,chat_history值为历史记录,默认为空数组。
const newQuestion = "我是vue新手,给我一些学习建议"
const res = await chain.call({
question:newQuestion,
chat_history:[]
})
console.log(res)
可以将上一次的问题和答案放进数组作为历史记录,一并喂给ChatGPT。
const secondRes = await chain.call({
question:"还有其他建议吗?",
chat_history:[newQuestion,res.text]
})
console.log(secondRes)
最后打印出ChatGPT返回的答案,表示成功。
转载自:https://juejin.cn/post/7234436390280233017