利用 PostgreSQL 构建 RAG 系统实现智能问答
在现代信息检索和自然语言处理的场景中,检索增强生成 (Retrieval-Augmented Generation, RAG) 系统因其结合了知识库检索和生成模型的优势,成为了一种非常流行的智能问答方法。在这篇博文中,我将展示如何利用PostgreSQL作为向量存储数据库,配合OpenAI嵌入模型和LangChain库,构建一个完整的RAG系统。
RAG 系统简介
RAG 系统的核心理念是:首先从知识库中检索与问题相关的文档或片段,然后通过生成式语言模型(如GPT)生成基于检索结果的答案。这种方法不仅提升了模型的问答准确性,还能够在多种场景中扩展大语言模型的应用。
在本文中,我们将使用:
- LangChain:一个为构建语言模型应用提供丰富工具的框架。
- PostgreSQL:作为存储文本片段及其嵌入向量的数据库。
- OpenAI API:为文档生成嵌入向量并使用 GPT 模型生成答案。
主要实现步骤
1. 环境准备与库的导入
我们首先需要导入必要的库,其中包括用于数据库连接的psycopg2,用于加载网页的bs4,以及LangChain相关的库。
import getpass
import os
import psycopg2
from psycopg2.extras import execute_values
import bs4
from langchain_community.document_loaders import WebBaseLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_openai import OpenAIEmbeddings
import numpy as np
from langchain_openai import ChatOpenAI
from langchain import hub
from langchain_core.output_parsers import StrOutputParser
2. 加载网页内容
我们将网页内容加载为文本,并使用LangChain提供的WebBaseLoader来解析网页,并提取需要的内容。
# 加载网页内容
loader = WebBaseLoader(web_paths=("https://lilianweng.github.io/posts/2023-06-23-agent/",),bs_kwargs=dict(parse_only=bs4.SoupStrainer(class_=("post-content", "post-title", "post-header"))),
)
docs = loader.load()
3. 文本切分
由于大语言模型处理较长文本时会受到限制,因此我们需要对加载的文本进行切分。在此处,使用RecursiveCharacterTextSplitter按照指定的字符大小将文本切分为多个块。
# 文本切分
text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
splits = text_splitter.split_documents(docs)
4. 连接 PostgreSQL 并存储嵌入向量
在这里,我们将利用PostgreSQL作为数据库,存储文本的嵌入向量。我们首先连接数据库,然后利用OpenAI的嵌入模型生成文本向量,并存储到数据库中。
# 连接到 PostgreSQL 数据库
conn = psycopg2.connect(host="localhost", # 根据需要更改database="mydb", # 更改为你的数据库名user="root", # 更改为你的用户名password=getpass.getpass("请输入你的 PostgreSQL 密码: ")
)
cur = conn.cursor()# 嵌入并存储向量
embedding_model = OpenAIEmbeddings()def store_vectors_in_pg(splits):embeddings = embedding_model.embed_documents([doc.page_content for doc in splits])data = [(0, 0, doc.page_content, embedding)for doc, embedding in zip(splits, embeddings)]insert_query = """INSERT INTO knowledge.vector_data_1 (user_id, file_id, content, featrue)VALUES %s"""execute_values(cur, insert_query, data)conn.commit()store_vectors_in_pg(splits)
5. 从数据库检索相似文档
通过查询PostgreSQL中的向量数据,基于余弦相似度查找与用户查询相似的文档。我们利用向量索引(HNSW)来高效检索相似文本片段。
# 检索相似文档
def retrieve_similar_docs(query, k=5):query_embedding = embedding_model.embed_query(query)embedding_str = f"'{str(query_embedding)}'"retrieve_query = f"""SELECT content, featrueFROM knowledge.vector_data_1ORDER BY featrue <-> {embedding_str}LIMIT %s"""cur.execute(retrieve_query, (k,))results = cur.fetchall()return [result[0] for result in results]
6. 构建 RAG 链并生成答案
我们使用LangChain的ChatOpenAI模型和Prompt链来完成生成答案的过程。
# 定义 RAG 链
prompt = hub.pull("rlm/rag-prompt")input_data = {"context": format_docs(retrieved_docs),"question": query
}# 构建 RAG 链
rag_chain = (prompt| ChatOpenAI(model="gpt-4o-mini")| StrOutputParser()
)# 生成答案
response = rag_chain.invoke(input_data)
print(response)
7. 关闭数据库连接
在程序结束时,别忘了关闭数据库连接。
cur.close()
conn.close()
完整代码实例
# 导入必要的库
import getpass
import os
import psycopg2
from psycopg2.extras import execute_values
import bs4
from langchain_community.document_loaders import WebBaseLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_openai import OpenAIEmbeddings
import numpy as np
from langchain_openai import ChatOpenAI
from langchain import hub
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough# 设置 OpenAI API 密钥
os.environ["OPENAI_API_KEY"] = getpass.getpass("请输入你的 OpenAI API 密钥: ")# 1. 加载网页内容
loader = WebBaseLoader(web_paths=("https://lilianweng.github.io/posts/2023-06-23-agent/",),bs_kwargs=dict(parse_only=bs4.SoupStrainer(class_=("post-content", "post-title", "post-header"))),
)
docs = loader.load()# 2. 切分文本
text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
splits = text_splitter.split_documents(docs)# 3. 连接到 PostgreSQL 数据库
conn = psycopg2.connect(host="localhost", # 根据需要更改database="mydb", # 更改为你的数据库名user="root", # 更改为你的用户名password=getpass.getpass("请输入你的 PostgreSQL 密码: ")
)
cur = conn.cursor()# 4. 嵌入并将分割后的文本和向量数据存储到 PostgreSQL
embedding_model = OpenAIEmbeddings()def store_vectors_in_pg(splits):# 生成嵌入向量embeddings = embedding_model.embed_documents([doc.page_content for doc in splits])# 将 numpy.ndarray 转换为 Python 列表data = [(0, 0, doc.page_content, embedding) # 假设 user_id 和 file_id 为 0,实际可以调整for doc, embedding in zip(splits, embeddings)]# 插入数据到 PostgreSQL 表 knowledge.vector_data_1insert_query = """INSERT INTO knowledge.vector_data_1 (user_id, file_id, content, featrue)VALUES %s"""execute_values(cur, insert_query, data)conn.commit()store_vectors_in_pg(splits)# 5. 从 PostgreSQL 中检索数据并计算相似度
def retrieve_similar_docs(query, k=5):# 将查询嵌入为向量query_embedding = embedding_model.embed_query(query)# 将 query_embedding 转换为 PostgreSQL 可识别的向量字符串格式embedding_str = f"'{str(query_embedding)}'" # 将list转为vector字符串# SQL 查询:通过 HNSW 索引查找最相似的文档retrieve_query = f"""SELECT content, featrueFROM knowledge.vector_data_1ORDER BY featrue <-> {embedding_str} -- 使用转换后的字符串进行余弦相似度计算LIMIT %s"""cur.execute(retrieve_query, (k,))results = cur.fetchall()return [result[0] for result in results] # 返回相似的内容# 6. 检索并生成答案
query = "任务分解是什么?"
retrieved_docs = retrieve_similar_docs(query)def format_docs(docs):return "\n\n".join(docs)# 定义 RAG 链
prompt = hub.pull("rlm/rag-prompt")# 构建 dict 输入
input_data = {"context": format_docs(retrieved_docs),"question": query
}# 构建 RAG 链
rag_chain = (prompt # 提示模板| ChatOpenAI(model="gpt-4o-mini") # 使用 OpenAI 的 LLM 模型生成答案| StrOutputParser() # 将输出解析为字符串格式
)# 生成答案
response = rag_chain.invoke(input_data)print(response)# 7. 关闭连接
cur.close()
conn.close()
结论
通过本文,我们展示了如何使用PostgreSQL作为向量存储的数据库,配合OpenAI嵌入模型及LangChain库构建一个简单的RAG系统。这个系统能够高效检索文本片段,并基于检索结果生成回答。RAG 系统在知识问答、信息检索等领域具有广泛的应用前景,尤其是在处理大量结构化或非结构化数据时,结合自然语言处理模型的强大生成能力,可以显著提升用户体验。
希望这篇文章能为你构建自己的RAG系统提供参考!
相关文章:
利用 PostgreSQL 构建 RAG 系统实现智能问答
在现代信息检索和自然语言处理的场景中,检索增强生成 (Retrieval-Augmented Generation, RAG) 系统因其结合了知识库检索和生成模型的优势,成为了一种非常流行的智能问答方法。在这篇博文中,我将展示如何利用PostgreSQL作为向量存储数据库&am…...
Go 并发模式:扩展与聚合的高效并行
当你搭建好一个管道系统后,数据在各个阶段之间顺畅地流动,并根据你设定的操作逐步转换。这一切看起来像是一条美丽的溪流,然而,为什么有时候这个过程会如此缓慢呢? 在处理数据时,某些阶段可能会非常耗时,导致上游的阶段被阻塞,无法继续处理数据。这不仅影响了管道的整…...
【Transformers基础入门篇2】基础组件之Pipeline
文章目录 一、什么是Pipeline二、查看PipeLine支持的任务类型三、Pipeline的创建和使用3.1 根据任务类型,直接创建Pipeline,默认是英文模型3.2 指定任务类型,再指定模型,创建基于指定模型的Pipeline3.3 预先加载模型,再…...
java反射学习总结
最近在项目上有一个内部的CR,运用到了反射。想起之前面试的时候被面试官追问有没有在项目中用过反射,以及反射的原理和对反射的了解。 于是借此机会,学习回顾一下反射,以及在项目中可能会用到的场景。 Java 中的反射概述 反射&…...
探索C语言与Linux编程:获取当前用户ID与进程ID
探索C语言与Linux编程:获取当前用户ID与进程ID 一、Linux系统概述与用户、进程概念二、C语言与系统调用三、获取当前用户ID四、获取当前进程ID五、综合应用:同时获取用户ID和进程ID六、深入理解与扩展七、结语在操作系统与编程语言的交汇点,Linux作为开源操作系统的典范,为…...
1.4 边界值分析法
欢迎大家订阅【软件测试】 专栏,开启你的软件测试学习之旅! 文章目录 前言1 定义2 选取3 具体步骤4 案例分析 本篇文章参考黑马程序员 前言 边界值分析法是一种广泛应用于软件测试中的技术,旨在识别输入值范围内的潜在缺陷。本文将详细探讨…...
Spring IOC容器Bean对象管理-注解方式
目录 1、Bean对象常用注解介绍 2、注解示例说明 1、Bean对象常用注解介绍 Component 通用类组件注解,该类被注解,IOC容器启动时实例化此类对象Controller 注解控制器类Service 注解业务逻辑类Respository 注解和数据库操作的类,如DAO类Reso…...
OpenAI API: How to catch all 5xx errors in Python?
题意:OpenAI API:如何在 Python 中捕获所有 5xx 错误? 问题背景: I want to catch all 5xx errors (e.g., 500) that OpenAI API sends so that I can retry before giving up and reporting an exception. 我想捕获 OpenAI API…...
C++初阶学习——探索STL奥秘——标准库中的priority_queue与模拟实现
1.priority_queque的介绍 1.priority_queue中文叫优先级队列。优先队列是一种容器适配器,根据严格的弱排序标准,它的第一个元素总是它所包含的元素中最大的。 2. 此上下文类似于堆,在堆中可以随时插入元素,并且只能检索最大堆元…...
PyTorch经典模型
PyTorch 经典模型教程 1. PyTorch 库架构概述 PyTorch 是一个广泛使用的深度学习框架,具有高度的灵活性和动态计算图的特性。它支持自动求导功能,并且拥有强大的 GPU 加速能力,适用于各种神经网络模型的训练与部署。 PyTorch 的核心架构包…...
C++ STL容器(三) —— 迭代器底层剖析
本篇聚焦于STL中的迭代器,同样基于MSVC源码。 文章目录 迭代器模式应用场景实现方式优缺点 UML类图代码解析list 迭代器const 迭代器非 const 迭代器 vector 迭代器const 迭代器非const迭代器 反向迭代器 迭代器失效参考资料 迭代器模式 首先迭代器模式是设计模式中…...
力扣416周赛
举报垃圾信息 题目 3295. 举报垃圾信息 - 力扣(LeetCode) 思路 直接模拟就好了,这题居然是中等难度 代码 public boolean reportSpam(String[] message, String[] bannedWords) {Map<String,Integer> map new HashMap<>()…...
vue 页面常用图表框架
在 Vue.js 页面中,常见的用于制作图表的框架或库有以下几种: ECharts: 官方网站: EChartsECharts 是一个功能强大、可扩展的图表库,支持多种图表类型,如柱状图、折线图、饼图等。Vue 集成: 可以使用 vue-echarts 插件,…...
spring 注解 - @PostConstruct - 用于初始化工作
PostConstruct 是 Java EE 5 中引入的一个注解,用于标注在方法上,表示该方法应该在依赖注入完成之后执行。这个注解是 javax.annotation 包的一部分,通常用于初始化工作,比如初始化成员变量或者启动一些后台任务。 在 Spring 框架…...
多机器学习模型学习
特征处理 import os import numpy as np import pandas as pd from sklearn.model_selection import train_test_split from sklearn.model_selection import StratifiedShuffleSplit from sklearn.impute import SimpleImputer from sklearn.pipeline import FeatureUnion fr…...
【网页设计】前言
本专栏主要记录 “网页设计” 这一课程的相关笔记。 参考资料: 黑马程序员:黑马程序员pink老师前端入门教程,零基础必看的h5(html5)css3移动端前端视频教程_哔哩哔哩_bilibili 教材:《Adobe创意大学 Dreamweaver CS6标准教材》《…...
STM32巡回研讨会总结(2024)
前言 本次ST公司可以说是推出了7大方面,几乎可以说是覆盖到了目前生活中的方方面面,下面总结下我的感受。无线类 支持多种调制模式(LoRa、(G)FSK、(G)MSK 和 BPSK)满足工业和消费物联网 (IoT) 中各种低功耗广域网 (LPWAN) 无线应…...
54 螺旋矩阵
解题思路: \qquad 这道题可以直接用模拟解决,顺时针螺旋可以分解为依次沿“右-下-左-上”四个方向的移动,每次碰到“边界”时改变方向,边界是不可到达或已经到达过的地方,会随着指针移动不断收缩。 vector<int>…...
基于STM32与OpenCV的物料搬运机械臂设计流程
一、项目概述 本文提出了一种新型的物流搬运机器人,旨在提高物流行业的物料搬运效率和准确性。该机器人结合了 PID 闭环控制算法与视觉识别技术,能够在复杂的环境中实现自主巡线与物料识别。 项目目标与用途 目标:设计一款能够自动搬运物流…...
[万字长文]stable diffusion代码阅读笔记
stable diffusion代码阅读笔记 获得更好的阅读体验可以转到我的博客y0k1n0的小破站 本文参考的配置文件信息: AutoencoderKL:stable-diffusion\configs\autoencoder\autoencoder_kl_32x32x4.yaml latent-diffusion:stable-diffusion\configs\latent-diffusion\lsun_churches-ld…...
使用VSCode开发Django指南
使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架,专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用,其中包含三个使用通用基本模板的页面。在此…...
超短脉冲激光自聚焦效应
前言与目录 强激光引起自聚焦效应机理 超短脉冲激光在脆性材料内部加工时引起的自聚焦效应,这是一种非线性光学现象,主要涉及光学克尔效应和材料的非线性光学特性。 自聚焦效应可以产生局部的强光场,对材料产生非线性响应,可能…...
React hook之useRef
React useRef 详解 useRef 是 React 提供的一个 Hook,用于在函数组件中创建可变的引用对象。它在 React 开发中有多种重要用途,下面我将全面详细地介绍它的特性和用法。 基本概念 1. 创建 ref const refContainer useRef(initialValue);initialValu…...
Oracle查询表空间大小
1 查询数据库中所有的表空间以及表空间所占空间的大小 SELECTtablespace_name,sum( bytes ) / 1024 / 1024 FROMdba_data_files GROUP BYtablespace_name; 2 Oracle查询表空间大小及每个表所占空间的大小 SELECTtablespace_name,file_id,file_name,round( bytes / ( 1024 …...
UDP(Echoserver)
网络命令 Ping 命令 检测网络是否连通 使用方法: ping -c 次数 网址ping -c 3 www.baidu.comnetstat 命令 netstat 是一个用来查看网络状态的重要工具. 语法:netstat [选项] 功能:查看网络状态 常用选项: n 拒绝显示别名&#…...
【解密LSTM、GRU如何解决传统RNN梯度消失问题】
解密LSTM与GRU:如何让RNN变得更聪明? 在深度学习的世界里,循环神经网络(RNN)以其卓越的序列数据处理能力广泛应用于自然语言处理、时间序列预测等领域。然而,传统RNN存在的一个严重问题——梯度消失&#…...
【大模型RAG】Docker 一键部署 Milvus 完整攻略
本文概要 Milvus 2.5 Stand-alone 版可通过 Docker 在几分钟内完成安装;只需暴露 19530(gRPC)与 9091(HTTP/WebUI)两个端口,即可让本地电脑通过 PyMilvus 或浏览器访问远程 Linux 服务器上的 Milvus。下面…...
如何在看板中有效管理突发紧急任务
在看板中有效管理突发紧急任务需要:设立专门的紧急任务通道、重新调整任务优先级、保持适度的WIP(Work-in-Progress)弹性、优化任务处理流程、提高团队应对突发情况的敏捷性。其中,设立专门的紧急任务通道尤为重要,这能…...
【服务器压力测试】本地PC电脑作为服务器运行时出现卡顿和资源紧张(Windows/Linux)
要让本地PC电脑作为服务器运行时出现卡顿和资源紧张的情况,可以通过以下几种方式模拟或触发: 1. 增加CPU负载 运行大量计算密集型任务,例如: 使用多线程循环执行复杂计算(如数学运算、加密解密等)。运行图…...
c#开发AI模型对话
AI模型 前面已经介绍了一般AI模型本地部署,直接调用现成的模型数据。这里主要讲述讲接口集成到我们自己的程序中使用方式。 微软提供了ML.NET来开发和使用AI模型,但是目前国内可能使用不多,至少实践例子很少看见。开发训练模型就不介绍了&am…...
