langchain实现的内部问答系统及本地化替代方案

主旨:问答系统搭建使用langchain要token,本文目的在于一、解析langchain调用过程,二、不使用langchain规避token,而使用本地化部署的方案方向。主要是本地向量化库的建立。

一、lagnchain问答系统实现优缺点

优点:几个python段落,方便

缺点:需要调用2次接口,需要外部模型token

系统实现:本地文档的问答

二、替代方案-逻辑验证

想法:拆解使用ai中使用token步骤,两个操作。寻找本地化替代方案即可

①问题拆解多样化,使得从不同角度解析问题,方便数据查找

②对使用文本、问题进行重组,以更人性化的方式输出(有寻找过不用这一步,但回答结果太多,不专注)

 # 1、MultiQueryRetriever
# 实现:INFO:langchain.retrievers.multi_query:Generated queries: ["1. How much does YiSuXianHua's salary pay its employees?", '2. What is the wage structure at YiSuXianHua?', '3. Can you provide information on the remuneration packages offered by YiSuXianHua?']

# 2、qa_chain链建立,是传入content 即qestion涉及所有内容(而不是整个向量库传递)

三、python代码-调用ai链版本(黄佳版本)

'''欢迎来到LangChain实战课
https://time.geekbang.org/column/intro/100617601
作者 黄佳'''

import os
# os.environ["OPENAI_API_KEY"] = '你的OpenAI API Key'
from dotenv import load_dotenv  # 用于加载环境变量
load_dotenv()  # 加载 .env 文件中的环境变量

# 1.Load 导入Document Loaders
from langchain_community.document_loaders import PyPDFLoader
from langchain_community.document_loaders import Docx2txtLoader
from langchain_community.document_loaders import TextLoader

# 加载Documents
# base_dir = '.\OneFlower' # 文档的存放目录
base_dir='/Volumes/G/projecttest/langchain-in-action/02_文档QA系统/OneFlower'
documents = []
for file in os.listdir(base_dir): 
    # 构建完整的文件路径
    file_path = os.path.join(base_dir, file)
    if file.endswith('.pdf'):
        loader = PyPDFLoader(file_path)
        documents.extend(loader.load())
    elif file.endswith('.docx'):
        loader = Docx2txtLoader(file_path)
        documents.extend(loader.load())
    elif file.endswith('.txt'):
        loader = TextLoader(file_path)
        documents.extend(loader.load())

# 2.Split 将Documents切分成块以便后续进行嵌入和向量存储
from langchain.text_splitter import RecursiveCharacterTextSplitter
text_splitter = RecursiveCharacterTextSplitter(chunk_size=200, chunk_overlap=10)
chunked_documents = text_splitter.split_documents(documents)

# 3.Store 将分割嵌入并存储在矢量数据库Qdrant中
from langchain_community.vectorstores import Qdrant
from langchain_openai import OpenAIEmbeddings
vectorstore = Qdrant.from_documents(
    documents=chunked_documents, # 以分块的文档
    embedding=OpenAIEmbeddings(), # 用OpenAI的Embedding Model做嵌入
    location=":memory:",  # in-memory 存储
    collection_name="my_documents",) # 指定collection_name

# 4. Retrieval 准备模型和Retrieval链
import logging # 导入Logging工具
from langchain_community.chat_models import ChatOpenAI # ChatOpenAI模型
from langchain.retrievers.multi_query import MultiQueryRetriever # MultiQueryRetriever工具
from langchain.chains import RetrievalQA # RetrievalQA链

# 设置Logging
logging.basicConfig()
logging.getLogger('langchain.retrievers.multi_query').setLevel(logging.INFO)

# 实例化一个大模型工具 - OpenAI的GPT-3.5
llm = ChatOpenAI(model_name="gpt-3.5-turbo", temperature=0)

# 实例化一个MultiQueryRetriever
retriever_from_llm = MultiQueryRetriever.from_llm(retriever=vectorstore.as_retriever(), llm=llm)

# 实例化一个RetrievalQA链
qa_chain = RetrievalQA.from_chain_type(llm,retriever=retriever_from_llm)

# 5. Output 问答系统的UI实现
from flask import Flask, request, render_template
app = Flask(__name__) # Flask APP

@app.route('/', methods=['GET', 'POST'])
def home():
    if request.method == 'POST':

        # 接收用户输入作为问题
        question = request.form.get('question')


        # RetrievalQA链 - 读入问题,生成答案
        result = qa_chain({"query": question})
        
        # 把大模型的回答结果返回网页进行渲染
        return render_template('index.html', result=result)
    
    return render_template('index.html')

if __name__ == "__main__":
    app.run(host='0.0.0.0',debug=True,port=5010)


#实战后备注:
 # 1、MultiQueryRetriever
# 实现:INFO:langchain.retrievers.multi_query:Generated queries: ["1. How much does YiSuXianHua's salary pay its employees?", '2. What is the wage structure at YiSuXianHua?', '3. Can you provide information on the remuneration packages offered by YiSuXianHua?']

# 2、qa_chain链建立,是传入content 即qestion涉及所有内容(而不是整个向量库传递)

#3、导入库的位置有改变

文档中无答案时候回答

在这里插入图片描述

文档中有答案时候回答

在这里插入图片描述

四、本地化部署(不用ai拆解方案)-仅方案

目标:我想实现文件的向量切分并存储在本地系统中。同时根据问题匹配度获取相应key的结果。想避免token的使用,免费的好的解决方案

4.1 代码

from sentence_transformers import SentenceTransformer
import numpy as np
import pickle
import os
from typing import List, Dict, Tuple

class LocalVectorSearch:
    def __init__(self, model_name: str = 'paraphrase-multilingual-MiniLM-L12-v2'):
        """
        初始化向量搜索系统
        Args:
            model_name: sentence-transformers模型名称,支持多语言
        """
        self.model = SentenceTransformer(model_name)
        self.vectors = {}  # 存储文档向量
        self.chunks = {}   # 存储文档片段
        
    def process_document(self, 
                        file_path: str, 
                        chunk_size: int = 500,
                        overlap: int = 50) -> None:
        """
        处理文档并存储向量
        Args:
            file_path: 文档路径
            chunk_size: 文档分块大小
            overlap: 重叠部分大小
        """
        # 读取文档
        with open(file_path, 'r', encoding='utf-8') as f:
            text = f.read()
            
        # 分块
        chunks = self._split_text(text, chunk_size, overlap)
        
        # 计算向量
        vectors = self.model.encode(chunks)
        
        # 存储
        doc_id = os.path.basename(file_path)
        self.vectors[doc_id] = vectors
        self.chunks[doc_id] = chunks
        
    def _split_text(self, 
                    text: str, 
                    chunk_size: int,
                    overlap: int) -> List[str]:
        """
        将文本分割成重叠的块
        """
        words = text.split()
        chunks = []
        
        for i in range(0, len(words), chunk_size - overlap):
            chunk = ' '.join(words[i:i + chunk_size])
            chunks.append(chunk)
            
        return chunks
    
    def search(self, 
               query: str, 
               top_k: int = 3) -> List[Tuple[str, str, float]]:
        """
        搜索最相关的文档片段
        Args:
            query: 搜索查询
            top_k: 返回结果数量
        Returns:
            List of (doc_id, chunk_text, similarity_score)
        """
        # 计算查询向量
        query_vector = self.model.encode([query])[0]
        
        results = []
        # 对每个文档进行搜索
        for doc_id, doc_vectors in self.vectors.items():
            # 计算相似度
            similarities = np.dot(doc_vectors, query_vector)
            
            # 获取最相关的片段
            top_indices = np.argsort(similarities)[-top_k:]
            
            for idx in top_indices:
                results.append((
                    doc_id,
                    self.chunks[doc_id][idx],
                    float(similarities[idx])
                ))
        
        # 按相似度排序
        results.sort(key=lambda x: x[2], reverse=True)
        return results[:top_k]
    
    def save(self, path: str) -> None:
        """保存向量和文档片段到本地"""
        with open(path, 'wb') as f:
            pickle.dump((self.vectors, self.chunks), f)
            
    def load(self, path: str) -> None:
        """从本地加载向量和文档片段"""
        with open(path, 'rb') as f:
            self.vectors, self.chunks = pickle.load(f)

4.2 解析本地化方案

4.2.1 这个解决方案使用了以下开源组件:
  1. sentence-transformers库 - 提供多语言文本向量化能力
  2. NumPy - 用于向量计算和相似度匹配
  3. pickle - 用于本地存储向量数据

4.2.2 主要功能包括:

  1. 文档分块处理
  2. 向量化存储
  3. 相似度搜索
  4. 本地持久化

4.2.3 使用示例

# 初始化系统
searcher = LocalVectorSearch()

# 处理文档
searcher.process_document('document1.txt')
searcher.process_document('document2.txt')

# 保存向量到本地
searcher.save('vectors.pkl')

# 加载已有向量
searcher.load('vectors.pkl')

# 搜索相关内容
results = searcher.search("你的问题")
for doc_id, chunk, score in results:
    print(f"文档: {doc_id}")
    print(f"相关内容: {chunk}")
    print(f"相似度: {score}\n")

4.2.4 使用优点,注意事项

4.2.4.1 优点是:
  1. 完全本地运行,不需要联网
  2. 使用开源模型,无需付费
  3. 支持中文等多语言
  4. 可以持久化存储,重复使用
4.2.4.2 需要注意的是:
  1. 首次下载模型需要联网
  2. 根据文档量可能需要较大内存
  3. 向量化计算可能需要GPU来提升性能
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值