青训营X豆包MarsCode 技术训练营第一课 | 豆包MarsCode AI 刷题

59 阅读6分钟

在参与“易速鲜花”内部员工知识库问答系统的开发过程中,深刻体会到了LangChain作为大语言模型应用开发框架的强大与便利。项目旨在解决公司内部信息分散、更新不及时等问题,通过构建一个高效的问答系统帮助员工快速获取所需信息。以下将结合项目的具体实施流程,分享在学习和实践中的心得体会。

项目实施流程

首先,在项目启动阶段,明确了需求和目标。需要一个能够处理多种文档格式(如PDF、Word、TXT)并回答与公司内部知识相关问题的系统。为实现这一目标,首先需要将各种文档加载到系统中。

数据加载

在数据准备和载入阶段,利用LangChain提供的文档加载器,轻松将不同格式的文件导入到系统中。以下是加载文档的代码示例:

import os
from langchain.document_loaders import PyPDFLoader, Docx2txtLoader, TextLoader

# 设置文档目录
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())

在这一过程中,准确加载文档是确保后续文本处理和信息检索成功的基础。通过简洁的代码,成功将多个文档加载到一个列表中,为后续步骤做好了准备。

文本分割

接下来,使用RecursiveCharacterTextSplitter将加载的文档分割成较小的文本块。这个步骤非常关键,因为冗长的文档会影响后续的嵌入和检索效果。将文本切分成200字符左右的块,确保每个块都足够小,以便进行有效的嵌入和查询。

from langchain.text_splitter import RecursiveCharacterTextSplitter

text_splitter = RecursiveCharacterTextSplitter(chunk_size=200, chunk_overlap=10)
chunked_documents = text_splitter.split_documents(documents)

这一过程使我认识到文本处理的重要性,尤其是在处理非结构化数据时。

向量数据库存储

在数据存储阶段,使用OpenAI的嵌入模型将文本块转换为嵌入向量,并将这些向量存储在Qdrant向量数据库中。嵌入技术的应用使文本转化为计算机能够理解的数字形式,这一过程有助于理解“嵌入”这一概念。

以下是将分割后的文档存储到向量数据库的代码:

from langchain.vectorstores import Qdrant
from langchain.embeddings import OpenAIEmbeddings

vectorstore = Qdrant.from_documents(
    documents=chunked_documents,
    embedding=OpenAIEmbeddings(),
    location=":memory:",  # 使用内存存储
    collection_name="my_documents",
)

向量数据库的选择让我意识到,在项目中使用适合的工具的重要性。Qdrant作为开源向量数据库,支持高效的相似度检索,这对后续问题回答系统至关重要。

信息检索与回答生成

随后,构建了RetrievalQA链,以便在用户提出问题时从数据库中检索相关信息并生成回答。在这一过程中,深刻体会到大语言模型(如GPT-3.5)在自然语言处理中的强大能力。将用户的问题和检索到的相关文本片段一起传递给模型,能够生成准确且上下文相关的回答。

from langchain.chat_models import ChatOpenAI
from langchain.chains import RetrievalQA

llm = ChatOpenAI(model_name="gpt-3.5-turbo", temperature=0)
qa_chain = RetrievalQA.from_chain_type(llm=llm, retriever=vectorstore.as_retriever())

这一过程展示了大模型在信息提取和理解方面的优势,提升了对自然语言处理潜力的认识。

用户界面设计

最后,创建了一个简单的Flask应用来展示问答系统的功能。通过用户输入的问题,能够实时生成并展示答案。以下是Flask应用的基本结构:

from flask import Flask, request, render_template

app = Flask(__name__)

@app.route('/', methods=['GET', 'POST'])
def home():
    if request.method == 'POST':
        question = request.form.get('question')
        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)

通过直观的界面和及时的反馈,用户能够方便地获取所需信息,提升了系统的实用性。

个人思考与总结

通过这个项目,不仅学会了如何使用LangChain构建一个文档问答系统,更加深了对大语言模型和向量数据库的理解。在实际操作中,体会到将复杂概念简化为易于操作流程的重要性。LangChain框架的设计理念使得原本需要大量人工干预的任务变得高效而直观,这无疑为未来的开发工作提供了便利。

此外,也意识到在选择技术栈时,理解每个工具的特点和适用场景至关重要。未来希望继续探索更多的向量数据库和开源模型,甚至尝试将HuggingFace提供的模型与LangChain结合,进一步拓展问答系统的功能。

总的来说,这次实践让我对LangChain的应用有了更加深入的理解,同时也激发了对自然语言处理领域的兴趣。期待在今后的学习和项目中,继续探索这一领域的更多可能性。

以下是另外一个实例,使用开源模型的另一种方法

基于文档的问答系统实现流程

一个基于文档的问答(QA)系统通常遵循以下流程:

  1. 文档获取

    • 收集和准备需要用于问答的文档。这些文档可以是PDF、文本文件、网页内容等。
  2. 文本分割

    • 使用文本分割器(如 RecursiveCharacterTextSplitter)将长文档分割成较小的片段,以便于后续处理和存储。
  3. 向量化

    • 将每个文本片段转换为向量表示。这可以通过预训练的嵌入模型(如 Sentence Transformers)实现,向量化后的文本片段便于在向量数据库中存储和检索。
  4. 向量数据库存储

    • 将文本的向量表示存储到向量数据库中(如 Chroma),以便快速检索与用户查询相关的文本片段。
  5. 用户查询

    • 接收用户的查询,并将其向量化。
  6. 相似度检索

    • 在向量数据库中查找与用户查询向量最相似的文本片段,通常使用最近邻搜索算法。
  7. 答案生成

    • 将检索到的相关文本片段传递给大语言模型(如 google/flan-t5-x1),并生成最终的回答。
  8. 返回结果

    • 将生成的答案返回给用户。

使用 Chroma 向量数据库实现问答系统

以下是如何使用 Chroma 向量数据库实现该任务的基本步骤:

  1. 安装和导入库

    from langchain.document_loaders import TextLoader
    from langchain.text_splitter import RecursiveCharacterTextSplitter
    from langchain.embeddings import HuggingFaceEmbeddings
    from langchain.vectorstores import Chroma
    from langchain.llms import HuggingFacePipeline
    from transformers import pipeline
    
  2. 加载和分割文档

    # 假设你有一个文档 loader
    loader = TextLoader('path_to_document.txt')
    documents = loader.load()
    text_splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)
    split_docs = text_splitter.split_documents(documents)
    
  3. 向量化和存储到 Chroma

    embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2")
    vector_store = Chroma.from_documents(split_docs, embeddings)
    
  4. 设置问答模型

    qa_model = pipeline("text2text-generation", model="google/flan-t5-x1")
    
  5. 处理用户查询

    user_query = "你的问题"
    query_embedding = embeddings.embed_query(user_query)
    similar_docs = vector_store.similarity_search(query_embedding)
    context = " ".join([doc.page_content for doc in similar_docs])
    answer = qa_model(f"根据以下内容回答问题:{context} 问题是:{user_query}")[0]['generated_text']
    
  6. 返回答案

    • 将生成的答案返回给用户。

使用 Hugging Face 模型替代 GPT-3.5

在这个流程中,我们使用 google/flan-t5-x1 模型作为问答的生成部分,这样做可以降低成本并充分利用开源模型。通过使用 Hugging Face 提供的 transformers 库,我们能够灵活地调用不同的模型并根据需求进行调整。