16. LangChain实战项目2——易速鲜花内部问答系统

发布于:2025-03-04 ⋅ 阅读:(11) ⋅ 点赞:(0)

需求简介

易束鲜花企业内部知识库如下:

本实战项目设计一个内部问答系统,基于这些内部知识,回答内部员工的提问。

 

在前面课程的基础上,需要安装的依赖包如下:

pip install docx2txt
pip install qdrant-client
pip install flask

 初始化代码

import os
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI

load_dotenv()
ai_model = os.getenv("OPENAI_MODEL")
# 实例化一个大模型工具 
llm = ChatOpenAI(model_name=ai_model, temperature=0)

from langchain_community.embeddings import HuggingFaceBgeEmbeddings
embedings = HuggingFaceBgeEmbeddings(model_name='./BAAI/bge-large-zh-v1.5', model_kwargs={'device': 'cuda'})

import logging
logging.basicConfig()
logging.getLogger('langchain.retrievers.multi_query').setLevel(logging.INFO)

 这段代码实例化了一个deepseek的llm,bge-large-zh 的向量模型、日志组件

导入内部知识到向量数据库

# 加载Documents
base_dir = './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)

from langchain_community.vectorstores import Qdrant
vectorstore = Qdrant.from_documents(
    documents=chunked_documents,
    embedding=embedings,
    location=":memory:",
    collection_name="my_documents",
)

 内部知识在OneFlow文件夹中,包含了pdf文档、文本文档、word文档等格式,通过加载器加载到document中,然后使用分词器去分割,最后以内存的方式存入到Qdrant向量数据库中

构建查询的QA链

from langchain.retrievers.multi_query import MultiQueryRetriever
from langchain.chains import RetrievalQA

# 实例化一个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)

MultiQueryRetriever 核心机制与技术优势

一、核心功能
  1. 多视角查询扩展

    • 接收用户原始查询后,利用 LLM 自动生成多个语义相关但表达形式不同的子查询,例如针对模糊查询补充具体场景或同义词描述‌。
    • 每个子查询独立执行向量数据库检索,合并结果并去重,形成更全面的文档集合‌。
  2. 动态适配场景

    • 适用于用户输入模糊、语义范围广的场景(如开放性问题),通过多查询覆盖不同解读角度,降低因单一检索偏差导致的错误响应‌25。

二、技术实现原理
  1. 生成-检索-融合流程

    • 生成阶段‌:LLM 根据原始查询生成 3-5 个变体问题,例如将“气候变化的影响”扩展为“全球变暖的经济后果”“碳排放对生态系统的破坏”等‌。
    • 检索阶段‌:各子查询分别通过向量相似度计算从数据库召回 Top-K 文档‌。
    • 融合阶段‌:合并所有文档并按相关性排序,去重后返回最终结果集‌。
  2. 性能优化特性

    • 支持异步并发执行子查询检索,显著缩短整体响应时间‌2。
    • 可配置生成查询数量、LLM 温度参数(temperature)以平衡生成多样性与相关性‌。

三、典型应用场景
  1. 模糊语义解析

    • 当用户提问包含歧义术语(如“AI 的伦理问题”)时,自动生成“人工智能数据隐私风险”“机器学习算法偏见案例”等子查询,提升知识覆盖范围‌。
  2. 跨领域知识检索

    • 在垂直领域(如医疗、法律)中,通过多查询映射专业术语与通用表述,解决术语差异导致的检索遗漏问题‌

 

RetrievalQA 组件解析

一、核心功能与定位
  • 检索增强生成(RAG)‌:将外部知识库检索与语言模型生成能力结合,通过“先检索后回答”机制提升问答准确性‌13。
  • 适用场景‌:适用于需要结合结构化/非结构化数据(如文档、数据库)的问答系统,可解决大模型幻觉问题‌

启动服务器

# 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=5000)

 这里使用flask启动了一个服务,监听post请求,调用qa链,返回数据渲染到index.html

index.html文件内容如下:

<body>
    <div class="container">
        <div class="header">
            <h1>易速鲜花内部问答系统</h1>
            <img src="{{ url_for('static', filename='flower.png') }}" alt="flower logo" width="200">
        </div>
        <form method="POST">
            <label for="question">Enter your question:</label>

            <input type="text" id="question" name="question"><br>
            <input type="submit" value="Submit">
        </form>
        {% if result is defined %}
            <h2>Answer</h2>
            <p>{{ result.result }}</p>
        {% endif %}
    </div>
</body>

运行

输入查询的问题后,后台运行的输出如下