写点什么

基于自定义数据源的 LangChain 的聊天

作者:派大星
  • 2023-07-08
    辽宁
  • 本文字数:5361 字

    阅读完需:约 18 分钟

准备好 node,pnpm 环境。

初始化项目

先创建一个空的文件夹langchain-demo,执行命令以下命令初始化一个项目。


cd langchain-demo/pnpm init
复制代码


接下来,我们准备一个测试数据,然后将数据摄取到向量数据库中。具体步骤如下:首先在根目录下创建一个 ingest-data.js然后在项目根目录下执行如下命令:


# 安装LangChainpnpm i langchain
复制代码


安装完成后:修改 package.json 文件,如图所示,增加"type": "module"


读取数据

准备数据

先准备一个 markdown 格式的文件。可以直接在网上找一个,比如 Vue3 的官方文档。然后在页面文档选中一部分,打开 F12 通过输入命令$0.innerHTML并回车后即可获得数据。简单如图所示:




或者自行准备数据也可。将网页中爬取的文本内容 copy 到 html-to-markdown网站上,转换成 markdown。并将转换后的文件放在项目根目录下的vue3-document.md中,如图所示


读取数据

编辑我们在之前已经准备好的文件ingest-data.js,文件内容如下:


// 读取markdown文件import { UnstructuredLoader } from "langchain/document_loaders/fs/unstructured";
// 实例化 并传入数据的路径const unstructuredLoader = new UnstructuredLoader("./vue3-document.md");
// 读取文件,这是个promise 使用awaitconst docs = await unstructuredLoader.load()
console.log(docs)
复制代码


然后通过如下命令进行验证:


cd langchain-demo/node ingest-data.js
复制代码


执行成功结果如图所示:


切割数据

继续编写ingest-data.js具体如下:


// 1. 读取markdown文件import { UnstructuredLoader } from "langchain/document_loaders/fs/unstructured";
// 2. 文档拆分,将文档拆分成一块一块的// 2.1 导入langchain提供的拆分工具import { RecursiveCharacterTextSplitter } from "langchain/text_splitter"

// 1.1 实例化UnstructuredLoader 并传入数据的路径const unstructuredLoader = new UnstructuredLoader("./vue3-document.md");
// 1.2 读取文件,这是个promise 使用awaitconst rawDocs = await unstructuredLoader.load()// 1.3console.log(rawDocs)
// 2.2 实例化 RecursiveCharacterTextSplitterconst splitter = new RecursiveCharacterTextSplitter({ // 一段文本按照1000的大小去拆分 chunkSize: 1000, // 当前一段文本与上一段文本重叠的大小为200 chunkOverlap: 200});// 2.3 切分数据const docs = await splitter.splitDocuments(rawDocs);
// 打印console.log(docs);
复制代码


然后通过如下命令进行验证:


cd langchain-demo/node ingest-data.js
复制代码


成功结果如图所示:对比之前多了一个 loc: [Object]


接入向量数据库

这里选择的向量数据库是:pinecone具体可参考官网文档自行选择:


https://python.langchain.com/docs/get_started/introduction.html



根据官网安装手册进行安装


https://python.langchain.com/docs/modules/data_connection/vectorstores/integrations/pinecone



pip install pinecone-client openai tiktoken# dotenv用来读取环境变量 安装向量数据库pnpm install -S dotenv langchain @pinecone-database/pinecone
复制代码

将数据写入向量数据库

这里采用的是Pinecone向量数据库。首先我们要注册一个 Pinecone 的数据库。官网地址:


https://www.pinecone.io/


注册完成后我们需要创建一个Index,如图所示操作:




注意文中的数字 1536 不要写错。


同时我们要记住自己创建的Index Name并找到,API Key,以及environment 记下来,后面会用到。



完成上述操作后 接下来继续编辑ingest-data.js文件,如下所示:


// 1. 读取markdown文件import { UnstructuredLoader } from "langchain/document_loaders/fs/unstructured";
// 2. 文档拆分,将文档拆分成一块一块的// 2.1 导入 langchain 提供的拆分工具import { RecursiveCharacterTextSplitter } from "langchain/text_splitter";// 利用这个将数据放入向量数据库里面import { PineconeStore } from "langchain/vectorstores/pinecone"import { OpenAIEmbeddings } from "langchain/embeddings/openai"
// 3. 导入向量数据库import { PineconeClient } from "@pinecone-database/pinecone";
// 读取环境变量import dotenv from "dotenv";dotenv.config();

// 1.1 实例化UnstructuredLoader 并传入数据的路径const unstructuredLoader = new UnstructuredLoader("./vue3-document.md");
// 1.2 读取文件,这是个promise 使用awaitconst rawDocs = await unstructuredLoader.load();// 1.3//console.log(rawDocs);
// 2.2 实例化 RecursiveCharacterTextSplitterconst splitter = new RecursiveCharacterTextSplitter({ // 一段文本按照1000的大小去拆分 chunkSize: 1000, // 当前一段文本与上一段文本重叠的大小为200 chunkOverlap: 200});// 2.3 切分数据const docs = await splitter.splitDocuments(rawDocs);
// 打印//console.log(docs);
// 3.1 初始化 pineconeconst pineconeClient = new PineconeClient();await pineconeClient.init({ apiKey: process.env.PINECONE_API_KEY, environment: process.env.PINECONE_ENV,});
const pineconeIndex = pineconeClient.Index(process.env.PINECONE_INDEX);
try { PineconeStore.fromDocuments(docs, new OpenAIEmbeddings(),{ // 向量数据库的信息 pineconeIndex, textKey: "text", namespace: "teach-vue3-document", });}catch (e) { console.log(e)}
复制代码


同时在项目根目录下创建一个.env文件,文件内容如下:


OPENAI_API_KEY="your openai key"PINECONE_API_KEY="pinecone api key"PINECONE_ENV="environment"PINECONE_INDEX="index name"
复制代码


完成上述操作后,执行如下命令:


cd langchain-demo/node ingest-data.js
复制代码


运行成功结果如图所示:




通过 Pinecone 的控制台我们可以看到,文档数据已经成功的写入到了向量数据库中。

构建 chains

创建一个chain.js,内容如下:


import { ConversationalRetrievalQAChain } from "langchain/chains";// 导入大语言模型import {OpenAI} from "langchain/llms/openai"import { PineconeClient } from "@pinecone-database/pinecone";import { PineconeStore } from "langchain/vectorstores/pinecone"import { OpenAIEmbeddings } from "langchain/embeddings/openai"// 读取环境变量import dotenv from "dotenv";dotenv.config();
// 创建大语言模型const model = new OpenAI({ // 准确度,越小越精准,越大越具有创造性 temperature: 0,});
// 创建向量数据库const pineconeClient = new PineconeClient();await pineconeClient.init({ apiKey: process.env.PINECONE_API_KEY, environment: process.env.PINECONE_ENV,});const pineconeIndex = pineconeClient.Index(process.env.PINECONE_INDEX);const pineconeStore = await PineconeStore.fromExistingIndex(new OpenAIEmbeddings(), { // 向量数据库的信息 pineconeIndex, textKey: "text", namespace: "teach-vue3-document",});
const chain = ConversationalRetrievalQAChain.fromLLM( model, pineconeStore.asRetriever(),{ // 返回信息是参考了什么资料的开关 sourceDocuments: returnSourceDocuments: true, });const res = await chain.call({ question: "vue3 如何为为 reactive() 标注类型 请给我写一个demo", // 聊天历史,也就是记忆的功能 chat_history: [],});
console.log(res);
// langchain 0.90版本这样写有问题,具体可参考:
// const secondRes = await chain.call({// question: "能否提供更多的demo",// // 聊天历史,也就是记忆的功能// chat_history: [// "vue3 如何为 ref() 标注类型 请给我写一个demo",// res.text// ],// });
console.log(secondRes);
复制代码


完成上述操作后,执行如下命令:


cd langchain-demo/node ingest-data.js
复制代码


运行结果如下:



其实我个人认为这个结果不如人意,但是至少这样做的方向是正确的。

构建后端

  1. 使用 koa-setup 构建后端


# 安装koa-setupnpm install -g koa-setup
koa-setup
复制代码


如图:



  1. 然后修改生成的server文件夹中的package.json文件中的 type 为 module。并添加**"dev": "nodemon index.js"**如图:



  1. 修改 server 文件夹下的index.js文件,如下:



import Koa from "koa"import Router from "koa-router"import { koaBody } from "koa-body";

// const Koa = require("koa")// const Router = require("koa-router")// const koaBody = require("koa-body")

const app = new Koa()
app.use(koaBody({ multipart:true}))

const router = new Router()router.get("/",(ctx)=>{ ctx.body = "hello server"})app.use(router.routes())

app.listen(8080,()=>{ console.log("open server localhost:8080")})
复制代码


  1. 安装 nodemon


cd server/pnpm i nodemon -D
复制代码


nodemon 类似于热部署,修改代码无需重新启动。



  1. 启动项目


pnpm dev
复制代码


启动成功:



构建 API

chat.js ingest-data.js vue3-document.md .env文件移动到 server 文件夹下,如图:



然后修改 index.js 和 chat.js,详情如下:index.js


import Koa from "koa"import Router from "koa-router"import { koaBody } from "koa-body";import { chat } from "./chain.js";

// const Koa = require("koa")// const Router = require("koa-router")// const koaBody = require("koa-body")

const app = new Koa()
app.use(koaBody({ multipart:true}))

const router = new Router()router.get("/",(ctx)=>{ ctx.body = "hello server"})
// 编写接口router.post("/chat",async (ctx) => { const { message } = ctx.request.body; const result = await chat(message)
ctx.body = { data: result, state: 1, }})
app.use(router.routes())

app.listen(8080,()=>{ console.log("open server localhost:8080")})
复制代码


chat.js


import { ConversationalRetrievalQAChain } from "langchain/chains";// 导入大语言模型import {OpenAI} from "langchain/llms/openai"import { PineconeClient } from "@pinecone-database/pinecone";import { PineconeStore } from "langchain/vectorstores/pinecone"import { OpenAIEmbeddings } from "langchain/embeddings/openai"// 读取环境变量import dotenv from "dotenv";dotenv.config();
// 创建大语言模型const model = new OpenAI({ // 准确度,越小越精准,越大越具有创造性 temperature: 0,});
// 创建向量数据库const pineconeClient = new PineconeClient();await pineconeClient.init({ apiKey: process.env.PINECONE_API_KEY, environment: process.env.PINECONE_ENV,});const pineconeIndex = pineconeClient.Index(process.env.PINECONE_INDEX);const pineconeStore = await PineconeStore.fromExistingIndex(new OpenAIEmbeddings(), { // 向量数据库的信息 pineconeIndex, textKey: "text", namespace: "teach-vue3-document",});
const chain = ConversationalRetrievalQAChain.fromLLM( model, pineconeStore.asRetriever(),{ // 返回信息是参考了什么资料的开关 sourceDocuments: returnSourceDocuments: true, });
export async function chat(message) { const res = await chain.call({ question: message, // 聊天历史,也就是记忆的功能 chat_history: [], }); return res;}
复制代码


然后启动运行pnpm dev并使用 postman 访问接口 "/chat" 如图:

构建前端

pnpm create vite
复制代码



安装组件tailwindcss


cd client/

pnpm install -D tailwindcss@latest postcss@latest autoprefixer@latest
# 执行initnpx tailwindcss init -p
# 安装axios 后续使用pnpm i axios
# 安装 marked marked 是一个流行的 JavaScript 库,用于将 Markdown 文本转换为 HTMLpnpm install marked
pnpm install --save-dev @types/marked
pnpm install typescript --save-dev
复制代码


tailwind.config.js添加配置(参考官网:https://v2.tailwindcss.com/docs/guides/vue-3-vite


purge: ["./index.html", "./src/**/*.{vue,js,ts,jsx,tsx}"],
复制代码


修改 style.css 文件(参考官网:https://v2.tailwindcss.com/docs/guides/vue-3-vite


@tailwind base;@tailwind components;@tailwind utilities;
复制代码


然后在App.vue中编写 Hello World。


<script setup></script>
<template> <div class="text-blue-400"> Hello World </div></template>
<style scoped></style>
复制代码


启动


cd client/pnpm dev
复制代码


页面如下:



接下来编写视图,(前端组件、js 等)viteproxy 文档:https://cn.vitejs.dev/config/server-options.html


源码获取:微信搜索【码上遇见你】回复【langchain-demo】


上述文章也是依据网络整理的。侵权删之。


发布于: 刚刚阅读数: 4
用户头像

派大星

关注

微信搜索【码上遇见你】,获取更多精彩内容 2021-12-13 加入

微信搜索【码上遇见你】,获取更多精彩内容

评论

发布
暂无评论
基于自定义数据源的LangChain的聊天_#LangChain_派大星_InfoQ写作社区