当前位置: 首页 > news >正文

使用 LangChain 和 RAG 实现《斗破苍穹》文本问答系

大模型系列文章

在本篇文章中,我将介绍如何使用 LangChain 框架和 RAG(Retrieval-Augmented Generation) 技术构建一个基于《斗破苍穹》小说文本的问答系统。通过这个系统,我们可以对小说内容提出问题,并获得由大模型生成的答案。


使用 LangChain 和 RAG 实现《斗破苍穹》文本问答系

  • 大模型系列文章
  • 前言
  • 一、技术栈概览
  • 二、使用步骤
    • 1.加载与预处理文本
    • 2.构建嵌入模型与向量数据库
    • 3. 构建提示模板与检索链
    • 4. 执行查询函数
  • 完整代码


前言

《斗破苍穹》是一部非常受欢迎的中国网络小说,内容丰富、章节众多。为了更高效地检索和理解其中的信息,我们可以通过 RAG 技术,将原始文本与大语言模型结合起来,从而实现自然语言的问题回答。


一、技术栈概览

  • LangChain:用于构建 LLM 应用流程
  • TextLoader:加载本地 TXT 文本数据
  • RecursiveCharacterTextSplitter:将长文本分割为适合嵌入的小块
  • FAISS:用于向量数据库的构建与存储
  • OllamaLLM 和 OllamaEmbeddings:调用本地运行的大语言模型(如 deepseek-r1:1.5b 和 bge-m3)
  • create_retrieval_chain:构建 RAG 管道

二、使用步骤

1.加载与预处理文本

我们首先使用 TextLoader 加载本地的 .txt 文件(即《斗破苍穹》全文),并设置编码格式为 gb18030 以支持中文字符。

loader = TextLoader("斗破苍穹.txt", encoding='gb18030')
docs = loader.load()

接着使用 RecursiveCharacterTextSplitter 将文档切分为多个段落(chunk),每个 chunk 最多 500 个字符,重叠部分为 50 个字符,以保留上下文信息。

text_splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)
documents = text_splitter.split_documents(docs)

2.构建嵌入模型与向量数据库

由于 LangChain 并未提供官方的 Ollama 嵌入模型,我们自定义了一个 OllamaEmbeddings 类来调用本地 API 接口获取嵌入向量。

class OllamaEmbeddings(Embeddings):def __init__(self, model: str = "bge-m3", url: str = "http://localhost:11434/api/embeddings"):self.model = modelself.url = url

然后使用 FAISS 构建向量数据库。如果已有索引文件,则直接加载;否则重新构建并保存。

try:vector_store = FAISS.load_local(VECTOR_STORE_PATH, embeddings, allow_dangerous_deserialization=True)
except Exception as e:vector_store = FAISS.from_documents(documents, embeddings)vector_store.save_local(VECTOR_STORE_PATH)

3. 构建提示模板与检索链

我们使用 ChatPromptTemplate 定义输入格式,将检索到的上下文插入到提示词中供 LLM 使用。

prompt = ChatPromptTemplate.from_template("""使用以下上下文回答问题。<context>{context}</context>问题: {input}"""
)

再创建文档链和检索链,连接 LLM 和向量数据库:

document_chain = create_stuff_documents_chain(llm, prompt)
retriever = vector_store.as_retriever(search_kwargs={"k": 3})
retrieval_chain = create_retrieval_chain(retriever, document_chain)

4. 执行查询函数

最后定义 query_rag 函数,接受一个问题字符串,返回模型生成的回答:

def query_rag(question: str) -> str:response = retrieval_chain.invoke({"input": question})return response['answer']

示例:

if __name__ == "__main__":answer = query_rag("萧炎是谁")print(answer)

完整代码

# 导入必要的模块(保持不变)
from langchain_community.document_loaders import TextLoader  # 替换为 TextLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_community.vectorstores import FAISS
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.embeddings import Embeddings
from langchain.chains.combine_documents import create_stuff_documents_chain
from langchain.chains import create_retrieval_chain
from langchain_ollama import OllamaLLM
import requests
from typing import List# 加载 TXT 文件并提取内容
file_path = "斗破苍穹.txt"  # 替换为你的 txt 文件路径
loader = TextLoader(file_path, encoding='gb18030')  # 使用 TextLoader 加载 txt 文件
docs = loader.load()
# 分割文本为小块(chunk),以便嵌入处理
text_splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)
documents = text_splitter.split_documents(docs)# 自定义 Ollama 嵌入模型类(保持不变)
class OllamaEmbeddings(Embeddings):def __init__(self, model: str = "bge-m3", url: str = "http://localhost:11434/api/embeddings"):self.model = modelself.url = urldef embed_documents(self, texts: List[str]) -> List[List[float]]:embeddings = []for text in texts:emb = self._get_embedding(text)if emb:embeddings.append(emb)return embeddingsdef embed_query(self, text: str) -> List[float]:return self._get_embedding(text)def _get_embedding(self, text: str) -> List[float]:headers = {"Content-Type": "application/json"}data = {"model": self.model,"prompt": f"Represent this sentence for retrieval: {text}"}try:response = requests.post(self.url, headers=headers, json=data)response.raise_for_status()return response.json()['embedding']except Exception as e:print(f"嵌入生成失败: {e}")return []# 初始化嵌入模型并构建向量数据库(保持不变)
embeddings = OllamaEmbeddings(model="bge-m3")
VECTOR_STORE_PATH = "faiss_index_doupo"try:vector_store = FAISS.load_local(VECTOR_STORE_PATH, embeddings, allow_dangerous_deserialization=True)print("✅ 已加载本地向量数据库。")
except Exception as e:print("⚠️ 未找到本地向量数据库,正在构建新的数据库...")vector_store = FAISS.from_documents(documents[:200], embeddings)vector_store.save_local(VECTOR_STORE_PATH)print("✅ 新的向量数据库已保存至本地。")# 构建 LLM 模型及提示模板(保持不变)
llm = OllamaLLM(model="deepseek-r1:1.5b")prompt = ChatPromptTemplate.from_template("""使用以下上下文回答问题。<context>{context}</context>问题: {input}"""
)# 创建文档链和检索链(保持不变)
document_chain = create_stuff_documents_chain(llm, prompt)
retriever = vector_store.as_retriever()
retriever.search_kwargs = {"k": 5}
retrieval_chain = create_retrieval_chain(retriever, document_chain)def query_rag(question: str) -> str:"""执行 RAG 查询流程,返回模型的回答"""response = retrieval_chain.invoke({"input": question})return response['answer']
# 示例查询(保持不变)
if __name__ == "__main__":answer = query_rag("萧炎是谁")print(answer)

相关文章:

  • Ros(控制机器人运动)
  • c++提升
  • 系统巡检常见工作
  • Python多线程编程:从GIL锁到实战优化
  • UE 5 和simulink联合仿真,如果先在UE5这一端结束Play,过一段时间以后**Unreal Engine 5** 中会出现显存不足错误
  • 深入解析Java17核心新特性(增强NullPointerException、强封装 JDK 内部 API、伪随机数生成器增强)
  • Makefile基础入门:从编译小白到自动化构建达人
  • 沉金PCB电路板制造有哪些操作要点需要注意?
  • 线程的生命周期与数量设置
  • 【TCP/IP和OSI模型以及区别——理论汇总】
  • 【工具使用】STM32CubeMX-FreeRTOS操作系统-任务、延时、定时器篇
  • DINO-R1
  • C语言-指针基础概念
  • leetcode题解236:二叉树的最近公共祖先
  • Elasticsearch中什么是分析器(Analyzer)?它由哪些组件组成?
  • JS利用原型链实现继承
  • 【leetcode】9. 回文数
  • (每日一道算法题)求根节点到叶节点数字之和
  • Java-IO流之字符输出流详解
  • qiankun模式下 主应用严格模式,子应用el-popover 点击无效不显示
  • 出口商出口外贸流程/武汉seo优化代理
  • 代刷推广网站/seo系统
  • 交互式网站备案/花生壳免费域名注册
  • 主营商城网站建设/百度推广费用可以退吗
  • 网站自响应/百度资源共享
  • 专做展厅设计网站/南昌seo优化