LangChain教程 - RAG - PDF问答
系列文章索引
LangChain教程 - 系列文章
在现代自然语言处理(NLP)中,基于文档内容的问答系统变得愈发重要,尤其是当我们需要从大量文档中提取信息时。通过结合文档检索和生成模型(如RAG,Retrieval-Augmented Generation),我们可以构建强大的问答系统。本博客将详细介绍如何使用FastAPI和LangChain框架,创建一个基于PDF文档的RAG问答API。
一、背景
在许多实际应用中,用户可能需要基于大量的PDF文件进行快速的问答查询。LangChain作为一个强大的框架,支持将各种数据源与生成模型集成,而FastAPI则是一个轻量级的Web框架,适用于构建高性能的API。在本案例中,我们将使用FastAPI作为API服务端,LangChain来处理文档加载、文本切分、向量存储和问答生成任务。
二、技术栈
- FastAPI:用于构建Web服务。
- LangChain:提供构建问答系统的工具,涉及文档加载、文本切分、向量存储、RAG链构建等功能。
- Ollama Embeddings:用于将文本转换为向量。
- Chroma:用于存储和检索文本向量的数据库。
- Starlette:FastAPI的底层库,用于支持流式响应。
三、实现步骤
1. 环境配置
首先,我们需要安装必需的库。你可以通过以下命令来安装:
pip install fastapi langchain langchain-chroma langchain-ollama langchain-community starlette uvicorn nest_asyncio
安装完毕后,我们可以开始构建我们的API。
2. 加载PDF并处理文本
我们从一个PDF文件加载文档,并将其切分成适合处理的小块。这样可以更高效地将文本转化为向量,并存储到数据库中。
from langchain_community.document_loaders import PyPDFLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter# 设置PDF文件路径
pdf_path = "../../files/pdf/en/Transformer.pdf"# 加载PDF文档并分割文本
loader = PyPDFLoader(pdf_path)
docs = loader.load()# 使用递归文本切分器来切分文档
text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=100)
splits = text_splitter.split_documents(docs)
在这段代码中,PyPDFLoader
被用来加载PDF文件,而RecursiveCharacterTextSplitter
则将文档切分为多个小块,确保每个块之间有200个字符的重叠,以便保持上下文的连贯性。
3. 存储向量到数据库
接下来,我们使用Chroma
来存储文档的向量表示。我们利用OllamaEmbeddings
模型将文本块转化为向量,并将它们存储在Chroma数据库中。
from langchain_chroma import Chroma
from langchain_ollama import OllamaEmbeddings# 存储分割后的文档到向量数据库
vectorstore = Chroma.from_documents(documents=splits, embedding=OllamaEmbeddings(model="nomic-embed-text"))
这里,我们通过OllamaEmbeddings
将文档切分块转换为嵌入向量,并使用Chroma
将这些向量存储到数据库中。这样,我们就可以通过相似度检索来快速找到与用户查询相关的文档。
4. 构建检索器
为了支持从数据库中检索相关文档,我们将构建一个基于相似度搜索的检索器。
# 构建检索器
retriever = vectorstore.as_retriever(search_type="similarity", search_kwargs={"k": 3})
这段代码通过as_retriever
方法创建了一个检索器,能够基于向量的相似度从数据库中返回与查询最相关的文档。
5. 定义RAG链
我们使用RAG(检索增强生成)技术,将检索到的文档与生成模型(如ChatOllama
)结合,生成最终的答案。hub.pull("rlm/rag-prompt")
方法提供了一个预定义的RAG提示模板。
from langchain import hub
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
from langchain_ollama import ChatOllama# 定义RAG提示模板
prompt = hub.pull("rlm/rag-prompt")# 格式化检索到的文档
def format_docs(docs):return "\n\n".join(doc.page_content for doc in docs)# 定义RAG链
rag_chain = ({"context": retriever | format_docs, "question": RunnablePassthrough()}| prompt| ChatOllama(model="deepseek-r1:7b")| StrOutputParser()
)
在这里,我们将检索到的文档内容格式化为字符串,并将其与用户的查询一起传递到生成模型中,以生成最终的答案。
6. 生成答案和流式响应
我们定义了两个方法:一个是生成完整答案,另一个是生成流式响应。
import json# 生成答案函数
async def generate_answer(question: str):response = await rag_chain.ainvoke(question)return response# 生成流式响应
async def generate_streaming_response(question: str):async for chunk in rag_chain.astream(question): # 使用astream逐块获取响应yield json.dumps({"answer chunk": chunk}) + "\n" # 按流式返回每一块内容
在这部分代码中,generate_answer
方法会返回完整的答案,而generate_streaming_response
方法则返回流式响应,每次返回一个内容块。
7. 创建FastAPI应用
最后,我们使用FastAPI创建一个Web应用,提供一个POST接口来接收用户查询,并返回答案。
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from starlette.responses import StreamingResponse# 创建FastAPI应用
app = FastAPI()# 定义输入模型
class QueryModel(BaseModel):question: strstream: bool = False # 默认不流式返回# 创建POST路由处理查询
@app.post("/query/")
async def query_question(query: QueryModel):try:if query.stream:# 如果stream为True,使用流式响应return StreamingResponse(generate_streaming_response(query.question), media_type="text/json")else:# 否则直接返回完整答案answer = await generate_answer(query.question) # 使用await获取完整的答案return {"answer": answer}except Exception as e:raise HTTPException(status_code=500, detail=str(e))
此API提供了一个接口,用户可以通过发送带有问题的POST请求来获取答案。如果请求中指定stream
为True
,系统将返回流式的答案。
8. 启动(jupyter)
notebooks下载地址: https://github.com/flower-trees/langchain-example/blob/master/pdf/jupyter/chat_pdf_api.ipynb
import nest_asyncio
import uvicorn
nest_asyncio.apply()
uvicorn.run(app, host="127.0.0.1", port=8000)
9. 提问
curl -X POST http://127.0.0.1:8000/query/ \-H "Content-Type: application/json" \-d '{"question": "Why is masking necessary in the decoder’s self-attention mechanism?","stream": true}'
10. 清理向量数据库
vectorstore.delete_collection()
四、完整代码实例
代码下载地址: https://github.com/flower-trees/langchain-example/blob/master/pdf/chat_pdf_api.py
import json
from contextlib import asynccontextmanagerfrom fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from langchain import hub
from langchain_chroma import Chroma
from langchain_community.document_loaders import PyPDFLoader
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
from langchain_ollama import ChatOllama, OllamaEmbeddings
from langchain_text_splitters import RecursiveCharacterTextSplitter
from starlette.responses import StreamingResponse# 设置 PDF 文件路径
pdf_path = "../files/pdf/en/Transformer.pdf"# 加载 PDF 文档并分割文本
loader = PyPDFLoader(pdf_path)
docs = loader.load()
text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=100)
splits = text_splitter.split_documents(docs)# 存储分割后的文档到向量数据库
vectorstore = Chroma.from_documents(documents=splits, embedding=OllamaEmbeddings(model="nomic-embed-text"))# 构建检索器
retriever = vectorstore.as_retriever(search_type="similarity", search_kwargs={"k": 3})# 定义 RAG 提示模板
prompt = hub.pull("rlm/rag-prompt")# 格式化检索到的文档
def format_docs(docs):return "\n\n".join(doc.page_content for doc in docs)# 定义 RAG 链
rag_chain = ({"context": retriever | format_docs, "question": RunnablePassthrough()}| prompt| ChatOllama(model="deepseek-r1:7b")| StrOutputParser()
)print("RAG ready")# 生成答案函数
async def generate_answer(question: str):response = await rag_chain.ainvoke(question)return response# 生成流式响应
async def generate_streaming_response(question: str):async for chunk in rag_chain.astream(question): # 使用 astream 逐块获取响应yield json.dumps({"answer chunk": chunk}) + "\n" # 按流式返回每一块内容# 8. 清理向量数据库
def clear_vectorstore():vectorstore.delete_collection()@asynccontextmanager
async def lifespan(app: FastAPI):# 在应用启动时执行的代码yield# 在应用关闭时执行的代码clear_vectorstore()print("Vectorstore cleaned up successfully!")# 创建 FastAPI 应用
app = FastAPI(lifespan=lifespan)# 定义输入模型
class QueryModel(BaseModel):question: strstream: bool = False # 默认不流式返回# 创建 POST 路由处理查询
@app.post("/query/")
async def query_question(query: QueryModel):try:if query.stream:# 如果 `stream` 为 True,使用流式响应return StreamingResponse(generate_streaming_response(query.question), media_type="text/json")else:# 否则直接返回完整答案answer = await generate_answer(query.question) # 使用 await 获取完整的答案return {"answer": answer}except Exception as e:raise HTTPException(status_code=500, detail=str(e))# 启动 FastAPI 应用(适用于开发环境)
# uvicorn chat_pdf_api:app --reload
五、总结
通过本教程,我们展示了如何使用FastAPI和LangChain框架,结合检索增强生成(RAG)技术,构建一个基于PDF文档的问答系统。系统支持两种查询方式:普通的完整答案返回和流式答案返回。借助LangChain提供的强大工具集,我们能够轻松地实现文档加载、文本切分、向量存储与检索等功能。FastAPI则让我们能够高效地将这些功能封装为一个Web API,供用户使用。
在实际应用中,这种基于文档的问答系统可以广泛应用于客户支持、知识库管理、教育培训等领域,为用户提供智能化的答案生成服务。
相关文章:
LangChain教程 - RAG - PDF问答
系列文章索引 LangChain教程 - 系列文章 在现代自然语言处理(NLP)中,基于文档内容的问答系统变得愈发重要,尤其是当我们需要从大量文档中提取信息时。通过结合文档检索和生成模型(如RAG,Retrieval-Augment…...
Windows 图形显示驱动开发-WDDM 3.2-自动显示切换(十二)
API 更改 ADS 功能增加了以下公共 API 功能: 枚举系统中的多路复用器设备。查询有关多路复用器的信息,例如,它连接了哪些目标,以及当前切换到哪个目标。触发多路复用器切换。如何检测多路复用器是否已切换。 枚举系统中的多路复…...

《当齐天大圣踏入3A游戏世界:黑神话·悟空的破壁传奇》:此文为AI自动生成
国产 3A 游戏的破晓之光 2024 年 8 月 20 日,这一天注定被铭记在中国游戏发展的史册上。国产首款 3A 游戏《黑神话・悟空》震撼上线,犹如一颗重磅炸弹,在全球游戏市场掀起了惊涛骇浪。仅仅上线 3 小时,其同时在线人数便突破了 140 万,一举打破 Steam 纯单机游戏最高在线纪…...

Graphics View画一个可调速的风机(pyqt)
效果如图: 风机具备调节转速的功能,转速通过扇叶旋转的快慢来区别,共分为四档,其中零档为静止状态,而一、二、三档则依次增加转速。在代码中,BlowerWrapper 类包含了可旋转的扇叶、风机外框以及选项三个主要…...

基于django图书信息管理系统的搭建(增删改查)
✍django项目搭建教程 ☞ ----------------- 教程 本文主要讲解django如何连接数据库MySQL并且可视化展示,实现增删改查功能 目录 一. 创建django应用 二. 数据库配置 三. 查看数据库 四. 编写代码 4.1视图函数 4.2 配置URL 4.3创建模板文件 4.…...
Python 编程题 第四节:斐波那契数列、列表的复制、暂停后输出、成绩评级、统计字符
斐波那契数列 方法一(递归) def f(a):if a1:return 1elif a2:return 1else:return f(a-1)f(a-2) print(f(3)) 方法二(非递归) nint(input()) lst[1,1] for i in range(2,n1):lst.append(lst[i-1]lst[i-2]) print(lst[n-1]) 列…...

【华为OD机考】华为OD笔试真题解析(15)--异常的打卡记录
题目描述 考勤记录是分析和考核职工工作时间利用情况的原始依据,也是计算职工工资的原始依据,为了正确地计算职工工资和监督工资基金使用情况,公司决定对员工的手机打卡记录进行异常排查。 如果出现以下两种情况,则认为打卡异常…...
跟我学C++中级篇——定时器的设计
一、定时器 谈到定时器,理论上讲是各种语言和各种设计都无法避开的一个技术点。对于定时器来说,表面上就是一种时间间隔的处理约定,但对程序来说,可能就是设计层面、接口层面和库或框架以及系统应用的一个大集合。不同的系统&…...
HTTP 请求时传递多部分表单数据
HTTP 请求时传递多部分表单数据(multipart/form-data) --data-raw $------demo11111\r\nContent-Disposition: form-data; name"Filedata"; filename"截屏2025-02-27 15.45.46.png"\r\nContent-Type: image/png\r\n\r\n\r\n------d…...
第J3-1周:DenseNet算法 实现乳腺癌识别
文章目录 一、前言二、前期准备1.设置GPU2.划分数据集 三、搭建网络模型1.DenseLayer模块2.DenseBlock模块3.Transition模块4.构建DenseNet5.构建densenet121 四、训练模型1.编写训练函数2.编写测试函数3.正式训练 五、结果可视化1.Loss与Accuracy图2.模型评估 总结:…...

Mac 版 本地部署deepseek ➕ RAGflow 知识库搭建流程分享(附问题解决方法)
安装: 1、首先按照此视频的流程一步一步进行安装:(macos版)ragflowdeepseek 私域知识库搭建流程分享_哔哩哔哩_bilibili 2、RAGflow 官网文档指南:https://ragflow.io 3、RAGflow 下载地址:https://github.com/infi…...
【解决】OnTriggerEnter/OnTriggerExit 调用匿名委托误区的问题
开发平台:Unity 开发语言:CSharp 6.0 开发工具:Visual Studio 2022 问题背景 public void OnTriggerEnter(Collider collider) {output.OnInteractionNoticed () > OnInteractionTriggered?.Invoke(); }public void OnTriggerExit(C…...

vscode集成DeepSeek
vscode 扩展 安装 Cline Meet Cline,一个可以使用你的CLI和编辑器的AI助手。 得益于 Claude 3.5 Sonnet的代理编码功能,Cline 可以逐步处理复杂的软件开发任务。借助让他创建和编辑文件、探索大型项目、使用浏览器和执行终端命令(在您授予权限后)的工具&…...

MapReduce编程模型
MapReduce编程模型 理解MapReduce编程模型独立完成一个MapReduce程序并运行成功了解MapReduce工程流程掌握并描述出shuffle全过程(面试)独立编写课堂及作业中的MR程序理解并解决数据倾斜 1. MapReduce编程模型 Hadoop架构图 Hadoop由HDFS分布式存储、M…...

SQL server2022的详细安装流程以及简单使用
鉴于SQL Server2008R2版本过于老旧,本文主要讲述如何安装SQL Server 2022。 本文主要详细介绍SQL server2022的详细安装流程以及简单使用,以《数据库系统概论(第5版)》的第79页—第80页为例,详细介绍如何使用SQL serv…...

Linux的诞生:一场自由与协作的技术革命
Linux的诞生:一场自由与协作的技术革命 在今天的互联网世界,Linux几乎无处不在——从智能手机(Android内核)到超级计算机,从云计算平台到家用路由器,它的身影渗透在技术的各个角落。但这样一个改变世界的操…...
Pytorch为什么 nn.CrossEntropyLoss = LogSoftmax + nn.NLLLoss?
为什么 nn.CrossEntropyLoss LogSoftmax nn.NLLLoss? 在使用 PyTorch 时,我们经常听说 nn.CrossEntropyLoss 是 LogSoftmax 和 nn.NLLLoss 的组合。这句话听起来简单,但背后到底是怎么回事?为什么这两个分开的功能加起来就等于…...
Go入门之文件
以只读方式打开文件 package mainimport ("fmt""io""os" )func main() {file, err : os.Open("./main.go")defer file.Close()if err ! nil {fmt.Println(err)return}fmt.Println(file)var tempSlice make([]byte, 128)var strSlice…...

基因型—环境两向表数据分析——品种生态区划分
参考资料:农作物品种试验数据管理与分析 用于品种生态区划分的GGE双标图有两种功能图:试点向量功能图和“谁赢在哪里”功能图。双标图的具体模型基于SD定标和h加权和试点中心化的数据。本例中籽粒产量的GGE双标图仅解释了G和GE总变异的53.6%,…...
Leetcode2414:最长的字母序连续子字符串的长度
题目描述: 字母序连续字符串 是由字母表中连续字母组成的字符串。换句话说,字符串 "abcdefghijklmnopqrstuvwxyz" 的任意子字符串都是 字母序连续字符串 。 例如,"abc" 是一个字母序连续字符串,而 "ac…...

SpringBoot-17-MyBatis动态SQL标签之常用标签
文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...

XML Group端口详解
在XML数据映射过程中,经常需要对数据进行分组聚合操作。例如,当处理包含多个物料明细的XML文件时,可能需要将相同物料号的明细归为一组,或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码,增加了开…...

.Net框架,除了EF还有很多很多......
文章目录 1. 引言2. Dapper2.1 概述与设计原理2.2 核心功能与代码示例基本查询多映射查询存储过程调用 2.3 性能优化原理2.4 适用场景 3. NHibernate3.1 概述与架构设计3.2 映射配置示例Fluent映射XML映射 3.3 查询示例HQL查询Criteria APILINQ提供程序 3.4 高级特性3.5 适用场…...
TRS收益互换:跨境资本流动的金融创新工具与系统化解决方案
一、TRS收益互换的本质与业务逻辑 (一)概念解析 TRS(Total Return Swap)收益互换是一种金融衍生工具,指交易双方约定在未来一定期限内,基于特定资产或指数的表现进行现金流交换的协议。其核心特征包括&am…...

HBuilderX安装(uni-app和小程序开发)
下载HBuilderX 访问官方网站:https://www.dcloud.io/hbuilderx.html 根据您的操作系统选择合适版本: Windows版(推荐下载标准版) Windows系统安装步骤 运行安装程序: 双击下载的.exe安装文件 如果出现安全提示&…...
爬虫基础学习day2
# 爬虫设计领域 工商:企查查、天眼查短视频:抖音、快手、西瓜 ---> 飞瓜电商:京东、淘宝、聚美优品、亚马逊 ---> 分析店铺经营决策标题、排名航空:抓取所有航空公司价格 ---> 去哪儿自媒体:采集自媒体数据进…...
聊一聊接口测试的意义有哪些?
目录 一、隔离性 & 早期测试 二、保障系统集成质量 三、验证业务逻辑的核心层 四、提升测试效率与覆盖度 五、系统稳定性的守护者 六、驱动团队协作与契约管理 七、性能与扩展性的前置评估 八、持续交付的核心支撑 接口测试的意义可以从四个维度展开,首…...

AI+无人机如何守护濒危物种?YOLOv8实现95%精准识别
【导读】 野生动物监测在理解和保护生态系统中发挥着至关重要的作用。然而,传统的野生动物观察方法往往耗时耗力、成本高昂且范围有限。无人机的出现为野生动物监测提供了有前景的替代方案,能够实现大范围覆盖并远程采集数据。尽管具备这些优势…...

android13 app的触摸问题定位分析流程
一、知识点 一般来说,触摸问题都是app层面出问题,我们可以在ViewRootImpl.java添加log的方式定位;如果是touchableRegion的计算问题,就会相对比较麻烦了,需要通过adb shell dumpsys input > input.log指令,且通过打印堆栈的方式,逐步定位问题,并找到修改方案。 问题…...

Qemu arm操作系统开发环境
使用qemu虚拟arm硬件比较合适。 步骤如下: 安装qemu apt install qemu-system安装aarch64-none-elf-gcc 需要手动下载,下载地址:https://developer.arm.com/-/media/Files/downloads/gnu/13.2.rel1/binrel/arm-gnu-toolchain-13.2.rel1-x…...