LangChain 基于 ChatGPT 构建本地知识库问答应用

news2024/11/18 20:13:53

一、使用本地知识库构建问答应用

上篇文章基于 LangChainPrompts 提示管理构建特定领域模型,如果看过应该可以感觉出来 ChatGPT 还是非常强大的,但是对于一些特有领域的内容让 GPT 回答的话还是有些吃力的,比如让 ChatGPT 介绍下什么是 LangChain

from langchain.llms import OpenAI
import os

openai_api_key=os.environ["OPENAI_API_KEY"]
llm = OpenAI(model_name="gpt-3.5-turbo", openai_api_key=openai_api_key)
my_text = "介绍下 langChain "
print(llm(my_text))

在这里插入图片描述

可以看出回答的貌似不是我们想要的内容,还有在一些特定的场景下的问答,比如学校学生问答系统中,学生提问周一课程是什么?,这种直接让 GPT 回答的话也是有些吃力,那这种情况下怎么解决呢?

现在有了 LangChain 那实现起来就非常简答了:

例如:现有知识内容放在了 data 目录下,有如下内容:

在这里插入图片描述
其中 txt 文件记录了 LangChain 的介绍:

在这里插入图片描述
pdf 介绍了下 langchain 中的 Prompts

在这里插入图片描述
csv 记录的学生课程信息:

在这里插入图片描述

下面首先对知识内容进行载入和向量化。

3.1 文本载入及 Embedding 向量持久化

import os
# 向量数据库
from langchain.vectorstores import Chroma
# 文档加载器
from langchain.document_loaders import TextLoader, CSVLoader, PyPDFLoader
# 文本转换为向量的嵌入引擎
from langchain.embeddings.openai import OpenAIEmbeddings
# 文本拆分
from langchain.text_splitter import RecursiveCharacterTextSplitter

openai_api_key = os.environ["OPENAI_API_KEY"]

knowledge_base_dir = "./data"

doc = []
for item in os.listdir(knowledge_base_dir):
    if item.endswith("txt"):
        loader = TextLoader(file_path=os.path.join(knowledge_base_dir, item), encoding="utf-8")
        doc.append(loader.load())
    elif item.endswith("csv"):
        loader = CSVLoader(file_path=os.path.join(knowledge_base_dir, item), encoding="utf-8")
        doc.append(loader.load())
    elif item.endswith("pdf"):
        loader = PyPDFLoader(file_path=os.path.join(knowledge_base_dir, item))
        doc.append(loader.load())

print("提取文本量:", len(doc))
# 拆分
text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=400)
docs = []
for d in doc:
    docs.append(text_splitter.split_documents(d))
    print("拆分文档数:", len(docs))
# 准备嵌入引擎
embeddings = OpenAIEmbeddings(openai_api_key=openai_api_key)
# 向量化
# 会对 OpenAI 进行 API 调用
vectordb = Chroma(embedding_function=embeddings, persist_directory="./cut")
for d in docs:
    vectordb.add_documents(d)
# 持久化
vectordb.persist()

运行后可以在 ./cut 看到持久化的向量内容:

在这里插入图片描述

3.2 构建问答

from langchain import OpenAI
# 向量数据库
from langchain.vectorstores import Chroma
from langchain.chains import RetrievalQA
# 文本转换为向量嵌入引擎
from langchain.embeddings.openai import OpenAIEmbeddings
import os

openai_api_key = os.environ["OPENAI_API_KEY"]
llm = OpenAI(temperature=0, openai_api_key=openai_api_key)

# 准备好你的嵌入引擎
embeddings = OpenAIEmbeddings(openai_api_key=openai_api_key)

vectordb = Chroma(embedding_function=embeddings, persist_directory="./cut")
# 创建您的检索引擎
qa = RetrievalQA.from_chain_type(llm=llm, chain_type="stuff", retriever=vectordb.as_retriever())

query = "介绍下什么是 langchain?"
res = qa.run(query)
print('问题:', query, 'LLM回答:', res)

query = "介绍下 langchain 中的 prompts ?"
res = qa.run(query)
print('问题:', query, 'LLM回答:', res)

query = "周一需要上什么课?"
res = qa.run(query)
print('问题:', query, 'LLM回答:', res)

query = "周三上午需要上什么课?"
res = qa.run(query)
print('问题:', query, 'LLM回答:', res)

在这里插入图片描述
可以看出已经精准的做出回答。

正常我们使用 ChatGPT 的时候都是以流的形式进行返回,同样这里我们也可以修改为流的形式:

from langchain import OpenAI
# 向量数据库
from langchain.vectorstores import Chroma
from langchain.chains import RetrievalQA
# 文本转换为向量的嵌入引擎
from langchain.embeddings.openai import OpenAIEmbeddings
# 流式回调
from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler
import os

openai_api_key = os.environ["OPENAI_API_KEY"]
llm = OpenAI(temperature=0, openai_api_key=openai_api_key, streaming=True, callbacks=[StreamingStdOutCallbackHandler()])

# 嵌入引擎
embeddings = OpenAIEmbeddings(openai_api_key=openai_api_key)

vectordb = Chroma(embedding_function=embeddings, persist_directory="./cut")
# 创建您的检索引擎
qa = RetrievalQA.from_chain_type(llm=llm, chain_type="stuff", retriever=vectordb.as_retriever())

query = "介绍下什么是 langchain?"
qa.run(query)

在这里插入图片描述

下面配合 tornado 高性能异步非阻塞web框架,实现接口调用问答。

二、部署 WEB 服务

安装 tornado 框架:

pip install tornado -i https://pypi.tuna.tsinghua.edu.cn/simple

创建问答服务接口 server.py

from tornado.concurrent import run_on_executor
from tornado.web import RequestHandler
import tornado.gen
import utils_response
from langchain import OpenAI
# 向量数据库
from langchain.vectorstores import Chroma
from langchain.chains import RetrievalQA
# 文本转换为向量的嵌入引擎
from langchain.embeddings.openai import OpenAIEmbeddings
import os

class QA(RequestHandler):
    # 准备模型
    openai_api_key = os.environ["OPENAI_API_KEY"]
    llm = OpenAI(temperature=0, openai_api_key=openai_api_key)
    # 准备好你的嵌入引擎
    embeddings = OpenAIEmbeddings(openai_api_key=openai_api_key)
    vectordb = Chroma(embedding_function=embeddings, persist_directory="./cut")
    # 检索引擎
    qa = RetrievalQA.from_chain_type(llm=llm, chain_type="stuff", retriever=vectordb.as_retriever())
    print("检索引擎已加载!")

    def prepare(self):
        self.executor = self.application.pool

    @tornado.gen.coroutine
    def get(self):
        questions = self.get_query_argument('questions')
        if not questions or questions == '':
            return utils_response.fail(message='问题为空')
        result = yield self.detection(questions)
        self.write(result)

    @run_on_executor
    def detection(self, questions):
        # 开始检测
        res = self.qa.run(questions)
        return utils_response.ok(res)

路由配置,并启动服务 app.py

import tornado.web
import tornado.ioloop
import tornado.httpserver
import os
from concurrent.futures.thread import ThreadPoolExecutor
from server import QA

## 配置
class Config():
    port = 8081
    base_path = os.path.dirname(__file__)
    settings = {
        # "debug":True,
        # "autore load":True,
        "static_path": os.path.join(base_path, "resources/static"),
        "template_path": os.path.join(base_path, "resources/templates"),
        "autoescape": None
    }

# 路由
class Application(tornado.web.Application):
    def __init__(self):
        handlers = [
            ("/qa", QA),
            ("/(.*)$", tornado.web.StaticFileHandler, {
                "path": os.path.join(Config.base_path, "resources/static"),
                "default_filename": "index.html"
            })
        ]
        super(Application, self).__init__(handlers, **Config.settings)
        self.pool = ThreadPoolExecutor(10)


if __name__ == '__main__':
    app = Application()

    httpserver = tornado.httpserver.HTTPServer(app)

    httpserver.listen(Config.port)

    print("start success", "prot = ", Config.port)

    print("http://localhost:" + str(Config.port) + "/")

    tornado.ioloop.IOLoop.current().start()

在这里插入图片描述
下面使用 PostMan 进行测试:

在这里插入图片描述
已经成功获取到答案。

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

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

相关文章

二重积分的解题技巧

计算方法 本节内容一般都应该先画图再思考后续内容较为直观 基本口诀是:后积先定限,限内画条线,先交写下限,后交写上限(且下限必须小于上限) 结合下图进行解释,后积先定限,对于X-型来…

Java基础-多线程JUC-多线程实现的三种形式

1. 第一种 继承Thread,重写run方法 public class demo1 {public static void main(String[] args) {/*** 多线程的第一种启动方式* 1. 定义一个类继承Thread* 2. 重写run方法* 3. 创建子类的对象,并启动线程*/MyThread myThread new MyThread();MyThrea…

受检异常和非受检异常

所谓的受检异常,表示在编译的时候强制检查的异常,这种异常需要显示的通过 try/catch 来捕捉,或者通过 throws 抛出去,否则从程序无法通过编译。 而非受检异常,表示在编译器可以不需要强制检查的异常,这种异…

Java基础---如何理解Java中的多态

目录 所谓多态 方法的重载与重写 重载和重写的区别 所谓多态 多态的概念比较简单,就是同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果如果按照这个概念来定义的话,那么多态应该是一种运行期的状态为了实现运行…

VScode 配置Orbslam debug调试环境(ubuntu18.04)

如果没有安装VScode的话,可以去点击官网下载 如果已经安装了VScode,可以直接把ORBSLAM的工程文件夹拖到VScode中,系统会提示是否添加C/C编译工具,直接安装即可,安装过程中也可以安装一下cmake 将ORBSLAM的工程文件夹拖…

obs编译

源码地址: GitHub - obsproject/obs-studio: OBS Studio - Free and open source software for live streaming and screen recording windows 编译: Build Instructions For Windows obsproject/obs-studio Wiki GitHub https://www.cnblogs.co…

app自动化测试(Android)--App 控件定位

客户端的页面通过 XML 来实现 UI 的布局,页面的 UI 布局作为一个树形结构,而树叶被定义为节点。这里的节点也就对应了要定位的元素,节点的上级节点,定义了元素的布局结构。在 XML 布局中可以使用 XPath 进行节点的定位。 App的布…

pytorch 迁移训练自己的数据集

1、pytorch 基础训练 上一节为基础 视频与AI,与进程交互(二) pytorch 极简训练自己的数据集并识别 接着上面一节,我们开始使用迁移学习,训练自己的数据集和保存网络,加载网络并识别。 2、 pytorch加载resnet18 RetNet网络的基础…

Vscode platformio Arduino开发STM32,点灯+串口调试

1.工具 USB-TTL(非常便宜,几块钱)STM32F103C8T6(几块钱) 2.引脚连线 USB-TTLSTM32TXPA10RXPA9VCC3.3VGNDGND 注意事项: 跳线帽位置:BOOT0接高电平(1),BOOT1接低电平(0)每次上传程序前需要按一下复位键(之后,跳线帽…

互联网编程之基于 TCP 的单线程文件收发程序(CS架构)

目录 需求 服务端实现 客户端实现 测试 需求 可试着根据java编程课所学到的java socket编程技术,尝试编写一个基于 TCP 的单线程文件收发程序,需满足: 服务端程序预先定义好需要发送的文件并等待客户端的连接。 客户端连接成功后&…

ranger配置hive出錯:Unable to connect repository with given config for hive

ranger配置hive出錯:Unable to connect repository with given config for hive 我一開始我以為是我重啟了ranger-admin導致ranger有點問題,後面排查之後發現是我之前把hiveserver2關閉了,所以只需要重新開啟hiveserver2即可

开源 sysgrok — 用于分析、理解和优化系统的人工智能助手

作者:Sean Heelan 在这篇文章中,我将介绍 sysgrok,这是一个研究原型,我们正在研究大型语言模型 (LLM)(例如 OpenAI 的 GPT 模型)如何应用于性能优化、根本原因分析和系统工程领域的问题。 你可以在 GitHub …

一个真实的社会工程学攻击

社会工程学实例 不同于以往通过心理诱骗暗示或欺诈手段社会工程学举例,本次为大家介绍一种特殊的结合刑侦推理及利用技术手段实现的社会工程学实例,可以把它归类为特殊层面的信息收集手段——通过照片确定发拍照人所在的位置,这种社工手段严格…

ExtJS4 相关

2. 程序架构 2.1 目录结构 推荐下面这种目录结构(非强制,如果你足够懂和自信) - appname- app- namespace- Class1.js- Class2.js- ...- extjs- resources- css- images- ...- app.js- index.html appname 包含所有程序代码,是根目录 app 包含所有类&…

MySQL基础篇(day03,复习自用)

MySQL第三天 排序与分页内容练习 多表查询内容练习 排序与分页 内容 #第五章 排序与分页#1.排序 #如果没有使用排序操作,默认情况下查询返回的数据是按照添加数据的顺序显示的。 SELECT * FROM employees;#1.1基本使用 #使用 ORDER BY 对查询到的数据进行排序操作 …

JSON轻量级数据交换格式

文章目录 前言前后端数据交换格式比较JavaScript自定义对象 前言 JSON全名 :JavaScriptObjectNoation JSON在后端和前端中起着非常重要的作用。 在前端,JSON被用作一种数据格式,用于从服务器获取数据并将其展示在网页上; 前端开…

[SpringBoot]单点登录

关于单点登录 单点登录的基本实现思想: 当客户端提交登录请求时,服务器端在验证登录成功后,将生成此用户对应的JWT数据,并响应到客户端 客户端在后续的访问中,将自行携带JWT数据发起请求,通常&#xff0c…

LeetCode 2501 数组中最长的方波 Java

方法一,哈希表枚举 构造哈希集合,记录出现过的数字枚举遍历 import java.util.HashSet; import java.util.Set;class Solution {public int longestSquareStreak(int[] nums) {//构造哈希表集合,记录出现过的数字,转long型&…

kafka入门,Leader 和 Follower 故障处理细节(十四)

Leader 和 Follower 故障处理细节 LEO:每个腹部最后一个offset,leo其实就是最新的offset1 HW:所有副本中最小的LEO Follower故障处理细节 (1)Follower发生故障会被临时提出ISR (2) 这个期间leader和Follower积蓄接收数据 (3) 待该Follower恢复后&#…

嵌入式基础知识

1.嵌入式系统的定义 以应用为中心,以计算技术为基础软硬件可裁剪,适应系统对功能、可靠性、成本、体积、功耗严格要求的专用计算机技术。 主要由嵌入式微控制器、外围硬件设备、嵌入式操作系统以及用户应用软件等部分分组成。 具有“嵌入式” 、“专用…