用LangChain实现一个ChatBlog

news2025/1/18 11:45:07

文章目录

  • 前言
  • 环境
  • 一、构建知识库
  • 二、将知识库向量化
  • 三、召回
  • 四、利用LLM做阅读理解
  • 五、效果
  • 总结


前言

通过本文, 你将学会如何使用langchain来构建一个自己的知识库问答
其实大多数类chatpdf产品的原理都差不多, 我将其简单粗暴地分为以下四步:

  1. 构建知识库
  2. 将知识库向量化
  3. 召回
  4. 利用LLM做阅读理解

下面, 我们就来看看, 如何利用我们自己写的博客, 打造一个ChatBlog

环境

老规矩, 环境是必不可少的一部分:

langchain==0.0.148
openai==0.27.4
chromadb==0.3.21

一、构建知识库

比较简单, 直接上代码吧

def get_blog_text():
    data_path = 'blog.txt'
    with open(data_path, 'r') as f:
        data = f.read()
    soup = BeautifulSoup(data, 'lxml')
    text = soup.get_text()
    return text


# 自定义句子分段的方式,保证句子不被截断
def split_paragraph(text, max_length=300):
    text = text.replace('\n', '') 
    text = text.replace('\n\n', '') 
    text = re.sub(r'\s+', ' ', text)
    """
    将文章分段
    """
    # 首先按照句子分割文章
    sentences = re.split('(;|。|!|\!|\.|?|\?)',text) 
    
    new_sents = []
    for i in range(int(len(sentences)/2)):
        sent = sentences[2*i] + sentences[2*i+1]
        new_sents.append(sent)
    if len(sentences) % 2 == 1:
      new_sents.append(sentences[len(sentences)-1])

    # 按照要求分段
    paragraphs = []
    current_length = 0
    current_paragraph = ""
    for sentence in new_sents:
        sentence_length = len(sentence)
        if current_length + sentence_length <= max_length:
            current_paragraph += sentence
            current_length += sentence_length
        else:
            paragraphs.append(current_paragraph.strip())
            current_paragraph = sentence
            current_length = sentence_length
    paragraphs.append(current_paragraph.strip())
    documents = []
    for paragraph in paragraphs:
        new_doc = Document(page_content=paragraph)
        print(new_doc)
        documents.append(new_doc)
    return documents

content = get_blog_text()
documents = split_paragraph(content)

这里必须要说明一下, 我没有使用langchain提供的文档划分函数, langchain提供了很多种文档划分方式, 感兴趣的同学可以查看 langchain.text_splitter里面的源码. 这里我给截出来了, 大概有这么些种吧, 其实都差不多, 目的都差不多是为了将段划分得比较合理.
请添加图片描述
我们这里设置了一个max_length, 这个长度, 如果使用的是chatgpt的话, 最大可以是4096, 因为chatgpt允许的最大输入Token4096, 换算成中文的话, 实际还要更短一些, 还要加上promptToken长度, 所以需要预留出一定空间.

分段分不好的话, 对输出的影响还是挺大的, 我们这里是按句划分, 其实更合理的是按博客的小标题划分, CSDN的问答机器人就是这么做的, 哈哈, 这里硬推一波, 效果还是很不错的, 超越了所有人类, 不服的可以来挑战一下:
https://ask.csdn.net/

后面我也会抽空写一篇CSDN问答机器人的博客来和大家分享一下实现细节, 点点关注不迷路

请添加图片描述

二、将知识库向量化

# 持久化向量数据
def persist_embedding(documents):
    # 将embedding数据持久化到本地磁盘
    persist_directory = 'db'
    embedding = OpenAIEmbeddings()
    vectordb = Chroma.from_documents(documents=documents, embedding=embedding, persist_directory=persist_directory)
    vectordb.persist()
    vectordb = None

这里的OpenAIEmbeddings默认使用的是text-embedding-ada-002模型来做emdedding, 你也可以换成别的, langchain提供了以下embedding的方式
请添加图片描述
你也可以自己从本地加载一个句向量模型去embedding, 这里需要注意一下, 如果使用的是openai的向量化模型的话, 是需要打开科学上网的.

向量化完了后, 我们需要将向量化后的结果存起来, 下次用 , 直接加载就行了, 我这里使用的是Chroma来存储向量化后的数据, 不过, langchain还支持其他的向量数据库, 如下:
请添加图片描述
Chroma我也是第一次用, 感兴趣的同学可以自己去了解一下, FAISS应该是用的比较多的, 我在问答机器人中用的是pgvector, 因为我们数据库用的是PostgresSQL, pgvector是PG的向量化存储插件, 所以我们用了这个, 并没有什么特别的原因, 其实各种向量化数据库都差不多, 影响召回速度和效果的是索引的构建方式, 其中比较知名的是HNSW, 感兴趣的可以去了解一下

三、召回

global retriever
def load_embedding():
    embedding = OpenAIEmbeddings()
    global retriever
    vectordb = Chroma(persist_directory='db', embedding_function=embedding)
    retriever = vectordb.as_retriever(search_kwargs={"k": 5})

k=5是指指召回top 5的结果

as_retriever函数还有个search_type的参数, 默认的是similarity, 参数解释如下:

search_type 搜索类型:“similarity” 或 “mmr”。search_type=“similarity” 在检索器对象中使用相似性搜索,在其中选择与问题向量最相似的文本块向量。search_type=“mmr” 使用最大边际相关性搜索,其中优化相似性以查询所选文档之间的多样性。

四、利用LLM做阅读理解

def prompt(query):
    prompt_template = """请注意:请谨慎评估query与提示的Context信息的相关性,只根据本段输入文字信息的内容进行回答,如果query与提供的材料无关,请回答"我不知道",另外也不要回答无关答案:
    Context: {context}
    Question: {question}
    Answer:"""
    PROMPT = PromptTemplate(
        template=prompt_template, input_variables=["context", "question"]
    )
    docs = retriever.get_relevant_documents(query)
    # 基于docs来prompt,返回你想要的内容
    chain = load_qa_chain(ChatOpenAI(temperature=0), chain_type="stuff", prompt=PROMPT)
    result = chain({"input_documents": docs, "question": query}, return_only_outputs=True)

    return result['output_text']

其实就是将召回的文本, 作为prompt的一部分, 再让chatgptprompt中总结答案, 跟阅读理解简直一模一样.
前面说的分段对结果影响很大, 在这个地方也有体现, 分段分不好, 召回的数据就不好, chatgpt就很难从中总结出答案.

注意: 这里也需要科学上网.

五、效果

请添加图片描述
非常正确

总结

1、整体跟阅读理解类似, 不过你可以调整prompt, 比如: 请你结合Context和你自己现有的知识, 回答以下问题
2、全部代码: https://github.com/seanzhang-zhichen/ChatBlog

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

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

相关文章

vue diff算法与虚拟dom知识整理(11) 书写patch父级新旧为同一节点 子节点与文字交换逻辑实现

上文我们简单描述了patch处理同一节点的大体逻辑 这次 我们就来看一下text替换的情况 我们更改案例入口文件 src下的 index.js 代码如下 import h from "./snabbdom/h"; import patch from "./snabbdom/patch";const container document.getElementById(…

Maven概念及搭建

1.为什么我们要学习 maven? maven 还未出世的时候&#xff0c;我们有很多痛苦的经历 。 痛点 1&#xff1a; jar 包难以寻找 痛点 2&#xff1a; jar 包依赖的问题 痛点 3&#xff1a; jar 不方便管理 痛点 4&#xff1a;项目编译 2.Maven 简介 Maven 是 Apache 软件基金…

Golang中的管道(channel) 、goroutine与channel实现并发、单向管道、select多路复用以及goroutine panic处理

目录 管道&#xff08;channel&#xff09; 无缓冲管道 有缓冲管道 需要注意 goroutine与channel实现并发 单向管道 定义单向管道 将双向管道转换为单向管道 单向管道作为函数参数 单向管道的代码示例 select多路复用 案例演示 goroutine panic处理 案例演示 管道…

APP服务端架构的演变

大家好&#xff0c;我是易安&#xff01; 早期2013年的时候&#xff0c;随着智能设备的普及和移动互联网的发展&#xff0c;移动端逐渐成为用户的新入口&#xff0c;各个电商平台都开始聚焦移动端App&#xff0c;如今经历了10年的发展&#xff0c;很多电商APP早已经没入历史的洪…

日语文法PPT截图31-45

31 形式名词 とき ところ 作为形式名词的话&#xff0c;一般是要写假名不写汉字的 相对时态 如果是一般时/将来时とき&#xff0c;就是先做后面的动作&#xff0c;在做前面的动作。 出教室的时候&#xff0c;关灯。 如果是过去时とき那么&#xff0c;是先做前面的动作&#…

Linux安装elk

稍后补充。 目录 01【安装elk】 es单机 es集群 esHead插件 kibana logstash elastic search:https://www.elastic.co/cn/downloads/elasticsearchlogstash:https://www.elastic.co/cn/downloads/logstashkibana:https://www.elastic.co/cn/downloads/kibana linux下安装E…

vector的介绍

vector的介绍&#xff1a;(vector翻译是向量&#xff0c;但是表示的是顺序表) vector是表示可以改变大小的数组的序列容器。 就像数组一样&#xff0c;vector对其元素使用连续的存储位置&#xff0c;这意味着也可以使用指向其元素的常规指针上的偏移量来访问它们的元素&#xf…

前端代码规范配置

前端代码规范配置 涉及到了eslint、prettier、husky、lint-staged等工具包的使用。 代码规则校验 使用eslint定义代码风格 安装eslint并在.eslintrc.js文件中配置。 npm i eslint -D这个代码风格可以使用公司团队内的规范&#xff0c;如果没有可以在github中找到一些主流的…

主机访问不到虚拟机(centos7)web服务的解决办法

目录 一、背景 二、解决办法 2.1、配置虚拟机防火墙 2.2、修改虚拟机网络编辑器 一、背景 主机可以访问外网&#xff0c;虚拟机使用命令&#xff1a;curl http://网址&#xff0c;可以访问到web服务 &#xff0c;主机使用http://网址&#xff0c;访问不到虚拟机&#xff08…

TikTok掀动出海淘金潮

嘉晟迪科&#xff1a;在各行各业都已经卷成红海的今天&#xff0c;最稀缺的是什么&#xff1f;当然是增长。那么&#xff0c;增长在哪里&#xff1f;流量在哪里&#xff0c;需求就在哪里&#xff0c;增长也就在那里。 因为短视频风靡全球的流行&#xff0c;内容平台特别是短视频…

Python-web开发学习笔记(2)--- HTML基础

先回顾一下上一篇文章&#xff1a;Python-web开发学习笔记&#xff08;1&#xff09;--- HTML基础_python web开发笔记_尚拙谨言的博客-CSDN博客 中讲了哪几个常用的HTML标签&#xff1a; <head>&#xff1a;头声明 <title>&#xff1a;网页标题 <h1>~<h6…

QT入门看这一篇就够了——超详细讲解(40000多字详细讲解,涵盖qt大量知识)

目录 一、Qt概述 1.1 什么是Qt 1.2 Qt的发展史 1.3 Qt的优势 1.4 Qt版本 1.5 成功案例 二、创建Qt项目 2.1 使用向导创建 2.2 一个最简单的Qt应用程序 2.2.1 main函数中 2.2.2 类头文件 2.3 .pro文件 2.4 命名规范 2.5 QtCreator常用快捷键 三、Qt按钮小程序 …

深度学习实战四:全连接神经网络(基于Pytorch,含数据和详细注释)

文章目录 概念softmax与交叉熵反向传播计算机视觉工具包torchvision全连接神经网络实现多分类概念 神经网络的第一层为输入层,最后一层为输出层,中间的所有层都叫做隐藏层 在计算神经网络层数时,一般不计算输入层,比如: 该神经网络的层数为2。输入层神经元有3个,隐藏层…

redis缓存数据库的使用

一&#xff0c;什么是redis &#xff1f;为什么要用它&#xff1f; 简单介绍&#xff1a; Redis是开源的key-value缓存框架&#xff0c;由c语言编写&#xff0c;也是一款高性能的框架提供多种语言的API 。 SET 每秒11万次 取get每秒81000次。 数据完全存储在内存空间中&…

【C++从0到王者】第八站:模板初阶

文章目录 一、泛型编程二、函数模板1.函数模板概念2.函数模板格式3.函数模板的原理4.函数模板的实例化1.隐式实例化2.显示实例化 5.模板参数的匹配原则 三、类模板1.类模板的格式2.类模板的实例化 一、泛型编程 当我们在写一个交换程序的时候 按照我们之前的想法&#xff0c;我…

谷歌算法快讯0519:近日排名变化频繁,排名或许回温?

从上周末到现在&#xff0c;已经有人注意到排名似乎又有了新的变化&#xff0c;根据WebMaster World上的帖子[1]和业内大家的讨论来看&#xff0c;大家共同的认识是5月16日开始就已经有变化&#xff0c;并且在5月19日的SEMRush Sensor来看已经到达峰值。 有一些在3月份谷歌更新…

yomichan使用笔记

导入词典词典下载 键盘快捷键 Alt Insert 打开搜索页面。 Alt DeleteToggle 打开/关闭扩展。 搜索结果中提供以下快捷方式&#xff1a; Esc取消当前搜索。 Alt PgUpPage 向上浏览结果。 Alt PgDnPage 向下浏览结果。 Alt End 转到最后一个结果。 Alt Home 转到第…

zookeeper的安装使用

zookeeper的安装使用 一、下载安装 https://zookeeper.apache.org/ 点击 download 以我自己的安装为例,linux,3.8.0 准备3台linux服务器&#xff1a;192.168.10.128、192.168.10.129、192.168.10.130 1.上传解压 把apache-zookeeper-3.8.0-bin.tar.gz 上传到 /usr/local/zo…

力扣sql中等篇练习(二十五)

力扣sql中等篇练习(二十五) 1 最繁忙的机场 1.1 题目内容 1.1.1 基本题目信息 1.1.2 示例输入输出 a 示例输入 b 示例输出 1.2 示例sql语句 # Write your MySQL query statement below WITH T as (SELECT t.airport_id,SUM(n) numFROM(SELECT departure_airport airport_i…

【C++进阶之路】内存管理

文章目录 一.内存管理1. 内存布局2. C的内存管理 ①内置类型② 自定义类型 3. operate new 与 operate delete ① 解读operate new源代码② 解读operate delete源代码 4. new和delete的基本原理①new对类对象②delete对类对象 拓展—— 深入理解delete[]和new[]对比 C和C内存…